Почему операции с хипом медленнее в Go, несмотря на поиск по памяти
Что проверяют
Понимаете ли вы, что дорога не сама аллокация, а её последствия. В Go выделить память в куче быстро, платить приходится потом, сборщику. Заодно смотрят, знаете ли вы про escape-анализ и умеете ли измерять.
Как отвечать
Сравните механику. На стеке выделение это сдвиг указателя, освобождение происходит само при возврате из функции. Данные лежат плотно и почти наверняка уже в кэше процессора. Стек горутины начинается маленьким и растёт копированием.
В куче аллокация тоже быстрая: память берут из локального для процессора кэша аллокатора, без блокировок. Разница набегает дальше. Каждый живой объект это работа для сборщика: его надо обойти при маркировке, а на время маркировки включаются барьеры записи, удорожающие присваивание указателя. Чем больше живых объектов, тем чаще циклы сборки, и это ровный рост загрузки процессора, а не одна медленная строка.
Второй фактор, локальность. Объекты в куче разбросаны, обход структуры по указателям даёт промахи кэша, и именно они съедают время. Срез структур по значению обгоняет срез указателей.
Что с этим делают: смотрят escape-анализ через go build -gcflags=-m, предвыделяют срезы и карты нужного размера, переиспользуют буферы, а результат подтверждают бенчмарком с -benchmem, где видно число аллокаций на операцию.
Где спотыкаются
Объясняют разницу фрагментацией и поиском свободного блока. Сборщик в Go не уплотняет память, но аллокатор режет её на классы размеров, и поиск блока узким местом не является. Узкое место это давление на сборщик, а фрагментация видна как рост потребления, а не как медленные аллокации.
Уверены, что new кладёт в кучу, а объявление переменной на стек. Решает компилятор по результату анализа побега: локальная переменная, адрес которой уходит наружу или которую кладут в интерфейс, окажется в куче, а new для значения, не покидающего функцию, останется на стеке.
Воюют с аллокациями там, где их единицы на запрос, и превращают код в набор пулов и общих буферов. Сначала профиль, потом правка. Рядом ждёт своя ловушка: буфер, возвращённый в пул и при этом оставшийся в чужой структуре, начинает жить в двух местах сразу.