Go 1.20 и арена памяти

от автора

Одной из революционных особенностей Go в сравнении с другими компилируемыми языками стало автоматическое управление освобождением памяти от неиспользуемых объектов (сборка мусора). В то же время она может привести к потере производительности при передаче контроля процессу управления памятью, но альтернативного механизма в Go представлено не было. Начиная с Go 1.20 появляется поддержка экспериментального решения для управления памятью, которое позволяет совместить безопасное выделение динамической памяти и уменьшить влияние интегрированного в скомпилированный код управления памятью на производительность приложения. В этой статье мы рассмотрим основные аспекты использования Memory Arena в Go 1.20.

Для запуска кода будем использовать актуальную на данный момент версию Go 1.20rc2, которая может быть получена из установочного пакета или через go install golang.org/dl/go1.20rc2@latest

Для включения поддержки нового механизма управления памятью добавим переменную окружения:

export GOEXPERIMENT=arenas

теперь для выделения памяти будем использовать новый модуль arena:

package main  import "arena"  type Person struct{   Lastname string   Firstname string }  func main() {   mem := arena.NewArena()   defer mem.Free()    for i:=0; i<10; i++ {     obj := arena.New[Person](mem)     print(obj)   } }

Как можно увидеть при запуске, адреса объектов будут выделяться последовательно из единой области памяти и (после вызова free) вся выделенная арена будет освобождаться. При правильном использовании это улучшает производительность кода, поскольку для арены не будет вызываться автоматическая сборка мусора. При необходимости копирования данных в обычный heap, может использоваться метод Clone, который создает копию структуры из арены в обычную динамическую память (например, при необходимости возврата результата обработки в основное приложение). Также в арене можно создавать слайсы с указанием начального размера и потенциальной емкости arena.MakeSlice(mem, initial, capacity).

Для выделения памяти на основе типа из reflect также можно использовать новый метод reflect.ArenaNew(mem, typ), который возвращает указатель на объект заданного типа, выделенный в арене, сохраненной в mem.

Обнаруживать ошибки при использовании арены (например, чтение или запись значения в структуру после освобождения арены) можно механизмами go run -asan (Address Sanitizer) или go run -msan (Memory Sanitizer), например:

package main  import "arena"  type Data struct {   value int32 }  func main() {   mem := arena.NewArena()   v := arena.New[Data](mem)   mem.Free()   v.value = 1 }

при запуске с asan/msan покажет ошибку некорректного использования указателя после освобождения арены. 

Для хранения строк в арене можно использовать создание области памяти из последовательности байт и копировать в нее содержимое строки, например так:

src := "original"  mem := arena.NewArena() defer mem.Free()  bs := arena.MakeSlice[byte](mem, len(src), len(src)) copy(bs, src) str := unsafe.String(&bs[0], len(bs))

Арена также может использоваться не только для хранения структур, но и для примитивных типов данных (или их последовательностей), в этом случае взаимодействие ничем не отличается от работы с указателем на переменную:

package main  import "arena"  func main() {   mem := arena.NewArena()   defer mem.Free()   v := arena.New[int32](mem)   *v = 10   println(*v) }

Аналогично поведение слайсов в арене не отличается от обычных слайсов в Go:

package main  import "arena"  func main() {   mem := arena.NewArena()   defer mem.Free()   v := arena.MakeSlice[int32](mem,50,100)   v[49] = 10;   v = append(v, 20)   println(v[49])//10   println(v[50])//20   println(len(v))//51   println(cap(v))//100 }

Для обнаружения утечек памяти при использовании арены можно использовать обычные механизмы профилирования в Go (go tool pprof для визуализации сэмплирования выделения памяти, которое может быть сохранено через функции модуля runtime/pprof). С точки зрения выделения памяти работа с ареной похожа на выделение одного блока памяти (который может увеличиваться в размере) и при освобождении арены все выделенные в ней объекты становятся недоступными.

Повышение производительности можно ожидать в тех случаях, когда приложение интенсивно выделяет память (например, при хранении двоичных деревьев или иных связанных структур данных), но при этом предполагается что выделенные структуры данных являются долгоживущими и существуют до момента освобождения арены целиком (сборщик мусора для арены не применяется и выделенные объекты в последовательной области памяти не очищаются). 

Статья подготовлена в преддверии старта курса Golang Developer. Professional. Приглашаю всех на бесплатный вебинар, где руководитель курса проведет собеседование выпускника программы. Реальные вопросы, комментарии по ответам, советы. Будет интересно.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/710124/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *