Go 1.27 ускоряет мелкие аллокации почти вдвое: 5,5 нс против 9,5

Go 1.27 ускоряет мелкие аллокации почти вдвое: 5,5 нс против 9,5

Go 1.27 получит почти двукратное ускорение выделения памяти под мелкие объекты. Переписывать код для этого не нужно, достаточно собрать проект новым компилятором.

Коротко про то, откуда берётся выигрыш. В Go есть стек и куча. Стек живёт в пределах функции: данные дописываются в конец, при выходе из функции память просто отбрасывается, поэтому управление ей почти бесплатное. С кучей сложнее: объект видят несколько горутин, порядок освобождения заранее неизвестен, память возвращает сборщик мусора.

В кучу попадает больше объектов, чем кажется на первый взгляд. Слайс вида make([]int, 100), который утекает за пределы функции, окажется там же. Указатель на локальную переменную, возвращённый из функции, тоже: в C или C++ компилятор выдал бы предупреждение про адрес локальной переменной, а Go спокойно переносит такую переменную в кучу.

Мелкие аллокации в куче выдаются не байт в байт. Go округляет запрошенный размер до ближайшего класса: 8, 16, 24, 32 байта и так далее. Сверху добавляются метаданные аллокатора, так что каждый объект стоит немного дороже своего размера.

До версии 1.27 компилятор знал размер объекта, но всё равно вызывал одну универсальную функцию, которая уже в рантайме искала класс размера и выбирала нужную ветку. В 1.27 для объектов меньше 80 байт появились специализированные функции под конкретные классы, и эта лишняя работа просто исчезла.

Цифры с MacBook автора: структура на 16 байт с указателем внутри выделяется за 5,5 наносекунды вместо 9,5 на Go 1.26. Получается прирост примерно в 1,8 раза на одной из самых горячих операций в языке.

Выигрыш почувствуют те, кто постоянно создаёт мелкие объекты: деревья, связные списки, парсеры, очереди событий, любые структуры, где каждая нода живёт отдельным объектом в куче. Сервисы, которые аллоцируют редко и крупными блоками, разницы почти не заметят. Автор честно об этом пишет.

Код бенчмарка лежит в репозитории автора: https://github.com/lemire/Code-used-on-Daniel-Lemire-s-

1