Кажется сложным написать целую статью о том, как инициализировать переменную и передавать ее через функции в Go. Дело в том, что это можно сделать несколькими способами.

Начнем с примера struct myStruct. и интерфейс myInterface:

const arraySize int64 = 1024 * 1024 * 1024

type myStruct struct {
   n int
   b [arraySize]byte
}

func (m *myStruct) N() int {
   return m.n
}

func (m *myStruct) set(n int) {
   m.n = n
}

type myInterface interface {
   N() int
   set(n int)
}

Используя функцию, вы можете, например, вернуть структуру, указатель на структуру или интерфейс:

func nonNamedStruct() myStruct {
   return myStruct{n: 5}
}
func nonNamedpointerToStruct() *myStruct {
   return &myStruct{n: 5}
}
func nonNamedInterface() myInterface {
   return &myStruct{n: 5}
}

Вы также можете назвать возвращаемые переменные. Это как-то влияет на производительность?

func namedPointerToStruct() (m *myStruct) {
   m = &myStruct{n: 5}
   return
}
func namedInterface() (m myInterface) {
   m = &myStruct{n: 5}
   return
}

func namedStruct() (m myStruct) {
   m.n = 5
   return
}

Итак, я хотел запустить тест, чтобы увидеть все возможные подходы к инициализации и передаче переменной размером 1 МБ, и знаете что? Результаты были совсем не впечатляющими ...

Тесты проводились на Go 1.9 на портативном компьютере MSI с питанием от Linux 4.11.9–1-ARCH на процессоре Intel (R) Core (TM) i7–6700HQ @ 2,60 ГГц и 16 ГБ ОЗУ.

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

Предыдущий график представляет собой сокращенную версию без двух самых медленных операций. Здесь мы можем понять, что самый быстрый способ создать эту структуру и вернуть ее в функции - это вернуть интерфейс, созданный с помощью new:

// Third position
func nonNamedInterface() myInterface {
   return &myStruct{n: 5}
}
// Second position
func namedPointerToStruct() (m *myStruct) {
   m = &myStruct{n: 5}
   return
}
// Fastest
func nonNamedInterfaceWithNew() myInterface {
   n := new(myStruct)
   n.n = 5
   return n
}

Вероятно, это не всегда так, и это сильно зависит от размера структуры и многих других аспектов. Давайте измерим скорость, если размер массива байтов равен 1:

Здесь мы видим, что самые медленные операции происходят при использовании интерфейса в качестве возвращаемого типа. Если сосредоточиться на самых быстрых подходах:

Мы видим, что простой возврат структур (вместо указателей) обычно выполняется быстрее, даже если указатель меньше (размер указателя всегда составляет 8 байтов).

А ты? Как вы обычно распределяете память в Go?

Бонусный раздел:

Я хотел бы поблагодарить команду Go за улучшения, внесенные в Go за последние годы. Здесь мы можем увидеть более медленный тест, сделанный в Go 1.5.4, который я сделал некоторое время назад:

Это было сделано с использованием структуры 128 Кбайт вместо 1 Мбайт. По какой-то причине я не мог воспроизвести его с 1 Мб, но уже ясно, что скорость на 1.5 была значительно ниже, чем сейчас, и менее стабильной.

Спасибо, команда Go!