Создано на базе изображений gopherize.me Довольно часто из Go кода нам приходится работать с различными HTTP API или самим выполнять роль HTTP сервиса. Один из частых случаев: получаем данные в виде структуры из базы данных, отправляем структуру внешнему API, в ответ получаем другую структуру, как-то её преобразуем и сохраняем в базу. Другими словами: такая обработка не требует множества отдельных операций со структурами запроса и ответа. Для API нормальна ситуация, когда в структурах запроса и ответа есть поля, которые могут быть nil и могут принимать какие-то не-nil значения. Такие структуры выглядят обычно так И, так как это ссылочный тип, то Go компилятор делает escape анализ и может перенести данную переменную в хип. В случае частого создания таких переменных — мы получаем лишнюю нагрузку на GC и даже можем получить "утечку памяти", если GC не успевает освободить всю использованную память. Что можно сделать в такой ситуации: Все бенчмарки воспроизводимы и размещены здесь. В Go коде мы обычно используем такие структуры со ссылочными типами Давайте сравним их со структурами с типами, которые передаются по значению Структура со ссылками обработана с 0 аллокаций, аналогично структуре со значениями.
Здесь мы даже видим, что структура со ссылками быстрее по времени обработки. Небольшое замечание: в данном бенчмарке мы видим две механики Go, которые объясняют эти (для кого-то неожиданные) результаты. Первое — инлайнинг функций. Это механизм, когда вызов функции заменяется на её тело. В данном случае для структур сработал этот механизм и это позволило избежать переноса структур в хип. Как результат — нет аллокаций. Во всех следующих бенчмарках мы не будем отключать этот механизм, т.к. хотим сравнить работу обычного Go кода. Второе — копирование структур с типами, которые передаются по значению. На копирование структур тратится дополнительное время, что мы и видим в результатах теста. Давайте сделаем бенчмарк с вызовом функций и передачей туда структур со ссылками и указателями. Как мы видим, структуры со ссылками обрабатываются всё-ещё быстрее структур со значениями и по-прежнему с нулевым количеством аллокаций. В сервисах часто используется JSON кодирование и декодирование структур. Давайте сравним результаты кодирования и декодирования структур, используя jsoniter. В данном бенчмарке ситуация меняется. Структуры со значениями быстрее и лучше по использованию памяти на одну операцию, лучше по количеству аллокаций. Давайте попробуем улучшить результаты кодирования и декодирования, используя easyjson. Почти все результаты для обеих структур лучше, кроме чуть большего использования памяти на одну операцию. Предварительный вывод: если в вашем коде длинная цепочка обработки полученного значения с итоговым результатом, то иногда будет лучше использовать ссылочные типы. Но если цепочка обработки вашего кода короткая (кодирование/декодирование значения) — то лучше использовать структуры со значениями. Пойдём дальше. Иногда структуры растут в размере Сделаем бенчмарк для этих структур. Далее мы видим, что для структуры со значениями обработка как и раньше даёт 0 аллокаций, но увеличилось время обработки (это нормально, т.к. структура стала больше). Так же, структура со ссылками потеряла преимущество: ненулевое количество аллокаций и значительно большее время обработки и использование памяти на одну операцию Попробуем передать данные структуры через цепочку вызовов функций. Для структур со ссылками ничего не изменилось. Для структур со значениями незначительно выросло время обработки (но всё ещё меньше, чем для структуры со ссылками). Попробуем сделать кодирование и декодирование. Структура со значениями лучше по всем параметрам Попробуем улучшить результат, используя easyjson. Структура со значениями лучше во всём. Структура со ссылками обрабатывается лучше, чем в jsoniter. Итоговый вывод: не делайте оптимизации на первом этапе разработки — лучше предпочесть использовать структуры со значениями, чем структуры со ссылками. И только когда производительность перестала устраивать — пройдите по цепочке обработки и попробуйте переключиться на передачу значений по ссылке в "горячих местах". Предпочтительно использовать кодогенераторы (easyjson и другие), чем обработку в коде — в большинстве случаев получим результаты лучше. Переключение выглядит просто — использовать Nullable типы. Пример из библиотеки sql — sql.NullBool, sql.NullString и другие. Так же, для типа потребуется описать функции кодирования и декодирования Как результат избавления от ссылочных типов в API — я разработал библиотеку nan, с основными Nullable типами с функциями кодирования и декодирования для JSON, jsoniter, easyjson, gocql. И один из последних вопросов, которые можно задать про переключение на Nullable типы — удобно ли их использовать. Моё личное мнение — удобно, у типов тот же паттерн использования что и у ссылок на переменые. При использовании ссылки мы пишем С Nullable типом мы пишем