Почему куча медленнее стека
Что проверяют
Знаете ли вы, что в Go размещение выбирает не программист, а анализ убегания в компиляторе, и понимаете ли, что настоящая цена кучи это не сам вызов аллокатора, а последующая работа сборщика мусора. Ответ уровня «стек это две быстрые операции» без упоминания сборщика считают поверхностным.
Как отвечать
Стек это указатель кадра: выделение это сдвиг, освобождение происходит при возврате из функции, бухгалтерии нет вовсе. Данные лежат плотно и попадают в кэш процессора. Куча требует аллокатора, который подбирает блок подходящего класса размера, и объекты расползаются по памяти, поэтому обход по указателям упирается в промахи кэша.
Отдельно проговорите сборщик. Само выделение в Go недорогое: у каждого исполнителя свой локальный кэш блоков, быстрый путь идёт без блокировок. Платим мы позже: каждый объект в куче добавляет работы фазе разметки, а во время разметки дорожает и запись указателей из-за барьера. Цена размазана по всей программе и в микрозамере одного выделения не видна.
Что заставляет значение убежать: возврат указателя на локальную переменную, упаковка в интерфейс, захват замыканием, которое уходит в горутину, слайс неизвестного при компиляции размера. Проверяется это сборкой с флагом -gcflags=-m.
func bad() *int { x := 1; return &x } // x escapes to heapПолезные приёмы: ёмкость в make заранее, небольшие структуры по значению, буферы через sync.Pool, замер бенчмарком с флагом -benchmem.
Где спотыкаются
Переносят модель из C и говорят, что new кладёт в кучу, а литерал в стек. В Go решает компилятор: new(T) спокойно остаётся на стеке, если указатель не убежал, а безобидная на вид переменная уезжает в кучу из-за интерфейса. Стек к тому же растёт динамически, фиксированного потолка в мегабайты у горутины нет.
Второе: сводят разницу к скорости вызова аллокатора. Основная часть счёта приходит от сборщика и от промахов кэша, а не от самого выделения.
Третье: оптимизируют вслепую. Указатели «чтобы не копировать» выталкивают данные в кучу и добавляют работы сборщику, а небольшая структура по значению почти всегда дешевле. И меряют это секундомером вместо бенчмарка, где видно число выделений на операцию.