Go: Должен ли я использовать указатель вместо копии моей структуры?
Last updated
Was this helpful?
Last updated
Was this helpful?
Перевод Иллюстрация, созданная для «A Journey With Go», из оригинального гофера, созданного Рене Френч. С точки зрения производительности систематическое использование указателей вместо копирования самой структуры для совместного использования структур многим Go разработчикам представляется наилучшим вариантом. Для того чтобы понять влияние использования указателя вместо копии структуры мы рассмотрим два варианта использования.
Давайте рассмотрим простой пример, когда вы хотите поделиться структурой для доступа к ее значениям:
Вот базовая структура, доступ к которой может быть разделен копией или указателем:
Основываясь на этих двух методах мы можем написать 2 бенчмарка. Первый — где структура передается копией:
Второй — очень похожий на первый — где структура передается по указателю:
Давайте запустим бенчмарки:
Получаем вот такую статистику:
Даже если этот пример немного экстремален, мы видим, как может быть дорого выделять переменную в куче, а не в стеке. В нашем примере структура намного быстрее аллоцируется в стеке и копируется, чем создается в куче и разделяется ее адрес.
Все может быть даже еще хуже, если мы ограничим процессор до 1 с помощью GOMAXPROCS=1:
Если бенчмарк размещения в стеке не изменился, то показатель в куче уменьшился с 75ns/op до 114ns/op.
Мы добавим два пустых метода в нашу структуру и немного адаптируем наши бенчмарки:
Бенчмарк с размещением в стеке создаст структуру и передаст ее копией:
И бенчмарк для кучи передаст структуру по указателю:
Как и ожидалось, результаты сейчас совсем другие:
Ссылка:
Использование копии структуры оказалось в 8 раз быстрее, чем использование указателя на нее! Чтобы понять почему, давайте посмотрим на графики, генерируемые трассировкой: график для структуры, переданной копией график для структуры, переданной указателем Первый график довольно прост. Поскольку не используется куча, нет сборщика мусора и лишней горутины. Во втором случае использование указателей заставляет компилятор Go и работать сборщику мусора. Если мы увеличим масштаб графика, то увидим, что сборщик мусора занимает важную часть процесса: На этом графике видно, что сборщик мусора запускается каждые 4 мс. Если мы снова увеличим масштаб, мы можем получить подробную информацию о том, что именно происходит: Синие, розовые и красные полосы являются фазами сборщика мусора, а коричневые связаны с аллоцированием в куче (на графике помечено как «runtime.bgsweep»):
Sweeping — это освобождение из кучи связанных с данными участков памяти, не помеченных как используемые. Это действие происходит, когда горутины пытаются выделить новые значения в памяти кучи. Задержка Sweeping добавляется к стоимости выполнения выделения в памяти кучи и не относится к каким-либо задержкам, связанным со сборкой мусора.
Если вы не знакомы со стеком/кучей, и если вы хотите больше узнать о их внутренних деталях, вы можете найти много информации в интернете, например, Пола Гриббла.
Использование указателя вместо копии структуры в go не всегда хорошо. Чтобы выбрать хорошую семантику для ваших данных, я настоятельно рекомендую прочитать пост о , написанной . Это даст вам лучшее представление о стратегиях, которые вы можете использовать со своими структурами и встроенными типами. Кроме того, профилирование использования памяти определенно поможет вам понять, что происходит с вашими аллокациями и кучей.