В этой статье я хочу объяснить вам, как внутренняя память работает в 64-битной архитектуре, когда вы создаете экземпляр структуры. (Вы можете поиграть с кодом https://play.golang.org/p/cUgB54yCpL)

Как память работает внутри:

Когда у вас есть такая структура:

type myStruct struct {
 myBool   bool    // 1 byte
 myFloat float64  // 8 bytes
 myInt  int32     // 4 bytes
}

Как видите, логическое значение занимает 1 байт, float64 - 8 байтов, а int32 - 4 байта.

Но в памяти выделяется последовательный пакет размером 8 байт. Итак, вместо того, чтобы брать 1 + 8 + 4 = 13 байт. Эта структура займет: 24 байта

a := myStruct{}
fmt.Println(unsafe.Sizeof(a)) // 24 bytes

Потому что в памяти у нас будет:

Итак, 8 + 8 + 8 = 24 байта

Как оптимизировать:

Можно оптимизировать, упорядочив структуру по взятым байтам:

type myStructOptimized struct {
 myFloat float64 // 8 bytes
 myBool  bool    // 1 byte
 myInt   int32   // 4 bytes
}

Эта новая упорядоченная структура теперь займет:

b := myStructOptimized{}
fmt.Println(unsafe.Sizeof(b)) // ordered 16 bytes

16 байт, потому что в памяти он будет размещен вот так (https://play.golang.org/p/gmkrt6X7aM):

8 + 8 = 16 байт

Как видите, для соблюдения выравнивания данных есть отступы (https://en.wikipedia.org/wiki/Data_structure_alignment). Это означает, что в некоторых случаях данные не оптимально выровнены по словам (в зависимости от во многих случаях процессор, кеш-память, виртуальная память), и иногда это может замедлить работу приложения, поэтому протестируйте все: D

Ссылки:

Особая благодарность за обзор сусликам канала #performance на Go Slack https://invite.slack.golangbridge.org/