Систематическое использование указателей для передачи структур вместо их копирования для многих разработчиков Go кажется лучшим вариантом с точки зрения производительности. Чтобы понять влияние использования указателя, как альтернативы передачи копии структуры, мы рассмотрим два юзкейса. Давайте рассмотрим простой пример, когда вам нужно поделиться структурой, чтобы передать куда-то ее значения: Вот небольшая структура, которую можно передать копией или по указателю: На основе этих двух методов мы можем написать 2 теста, в одном из которых будет передаваться копия структуры: И еще один, очень похожий, когда она передается по указателю: Запустим тесты: И вот результаты: Использование копии структуры здесь в 8 раз быстрее указателя. Чтобы понять, почему, давайте взглянем на графики, сгенерированные с помощью trace: График для структуры, переданной копированием График для структуры, переданной по указателю Первый график довольно прост, поскольку нет использования кучи, нет сборщика мусора и дополнительных горутин. Что касается второго графика — использование указателей заставляет компилятор go переместить переменную в кучу и создать давление на сборщик мусора. Если мы увеличим масштаб графика, то увидим, что сборщик мусора играет важную роль во всем процессе: На этом графике мы видим, что сборщик мусора должен отрабатывать каждые 4 мс. Если мы увеличим масштаб еще немного, мы сможем получить подробную информацию о том, что именно происходит: Синий, розовый и красный — это фазы сборки мусора, а коричневый — это выделение памяти в куче (на графике помечено как «runtime.bgsweep»): Sweeping — это когда восстанавливается память, связанная со значениями в динамической памяти, которые не были помечены как используемые (in-use). Это действие происходит, когда приложение Goroutines пытается выделить новые значения в динамической памяти. Задержка свипинга добавляется к стоимости выделения памяти в куче и не связана ни с какими задержками, связанными со сборкой мусора. https://www.ardanlabs.com/blog/2018/12/garbage-collection-in-go-part1-semantics.html Даже если этот пример немного экстремален, мы все-равно можем наблюдать, насколько дорого может обходиться аллокация переменной в куче, а не в стеке. В нашем примере код гораздо быстрее аллоцирует структуру в стеке и копирует ее, нежели размещает ее в куче и передает ее адрес. Если вы не знакомы со стеком/кучей и хотите больше узнать о внутреннем устройстве каждого из них, вы легко можете найти множество информации по этой теме в интернете (например статья Пола Гриббла). Ситуация была бы еще хуже, если бы мы ограничили процессор до 1 с GOMAXPROCS = 1: Если тест с аллокацией в стеке не меняется, то показатели для кучи ухудшились с 75нс/оп до 114нс/оп. В этом юзкейсе мы добавим в нашу структуру два пустых метода с небольшой адаптацией для наших тестов: Тест с аллокацией в стеке создаст структуру и передаст ее копией: Тест с кучей передаст структуру по указателю: Как и ожидалось, результаты теперь совсем другие: Использование указателя в качестве альтернативы копированию структуры в go — не всегда хорошая идея. Чтобы выбрать хорошую семантику для ваших данных, я настоятельно рекомендую прочитать статью о семантике значения/указателя, написанную Биллом Кеннеди. Это даст вам лучшее представление о стратегиях, которые вы можете использовать со своей структурой и встроенными типами. Кроме того, профилирование использования памяти определенно поможет вам выяснить, что происходит с вашими аллокациями и вашей кучей. Материал подготовлен в преддверии старта курса Golang Developer. Professional Переводчик, редактор