TGViewer
igoroutine.courses igoroutine.courses @igoroutine_courses · 239 subscribers
Post #38 696
😎😎 Это изменение сломало set на map[key]struct{}, который мы знали

Продолжение предыдущего поста (ссылка) 👍👍

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



type group struct {
ctrl uint64
slots [abi.SwissMapGroupSlots]struct {
key keyType
elem elemType
}
}


Изначальное расположение полей:

map[addrDetail]struct {}: key=24 elem=0 slot=32
map[any]struct {}: key=16 elem=0 slot=24
map[canceler]struct {}: key=16 elem=0 slot=24
map[*conn]struct {}: key=8 elem=0 slot=16
map[GoID]struct {}: key=8 elem=0 slot=16
map[*http2serverConn]struct {}: key=8 elem=0 slot=16
map[*net.Listener]struct {}: key=8 elem=0 slot=16
map[ProcID]struct {}: key=8 elem=0 slot=16
map[string]struct {}: key=16 elem=0 slot=24
map[_typePair]struct {}: key=16 elem=0 slot=24
map[uintptr]struct {}: key=8 elem=0 slot=16


Измененное расположение полей:

map[addrDetail]struct {}: key=24 elem=0 slot=24
map[any]struct {}: key=16 elem=0 slot=16
map[canceler]struct {}: key=16 elem=0 slot=16
map[*conn]struct {}: key=8 elem=0 slot=8
map[GoID]struct {}: key=8 elem=0 slot=8
map[*http2serverConn]struct {}: key=8 elem=0 slot=8
map[*net.Listener]struct {}: key=8 elem=0 slot=8
map[ProcID]struct {}: key=8 elem=0 slot=8
map[string]struct {}: key=16 elem=0 slot=16
map[_typePair]struct {}: key=16 elem=0 slot=16
map[uintptr]struct {}: key=8 elem=0 slot=8


Размер слотов уменьшился

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


// key returns a pointer to the key at index i.
func (g *groupReference) key(typ *abi.MapType, i uintptr) unsafe.Pointer {
offset := groupSlotsOffset + i*typ.SlotSize

return unsafe.Pointer(uintptr(g.data) + offset)
}

// elem returns a pointer to the element at index i.
func (g *groupReference) elem(typ *abi.MapType, i uintptr) unsafe.Pointer {
offset := groupSlotsOffset + i*typ.SlotSize + typ.ElemOff

return unsafe.Pointer(uintptr(g.data) + offset)
}


Полностью решить эту проблему разработчики обещают в Go 1.27.

UPD: Буквально сегодня замёржили фикс в master, полностью допилив решение

😎😎 А пока, если вас спросят на собеседовании, то знайте, что в Go 1.24+ map[K]struct{} больше не гарантирует экономию памяти относительно map[K]bool (для некоторых типов ключей размер slots оказывается таким же)

Ну а для тех, кто хочет комплексно изучить внутреннее устройство всех основных структур данных в Go, напоминаю, что в конце апреля стартует уникальный поток The Nature of Go.

👉👉 Link
👉👉 Link
Telegram igoroutine.courses 😎😎 Это изменение сломало Set на map[key]struct{}, который мы знали В Go все привыкли писать set вот так: type set[K comparable] struct { data map[K]struct{} } Ибо многие знают, что в Go пустая структура не занимает память. Давайте посмотрим на формулировку…
More from @igoroutine_courses
  1. Jun 25, 2026Разбор задачи с порядком вызова функций в select Что будет выведено в stdout после запуска…
  2. Jun 24, 2026Post #40
  3. Jun 24, 2026photo post
  4. Mar 23, 2026😎😎 Это изменение сломало Set на map[key]struct{}, который мы знали В Go все привыкли пис…
  5. Mar 5, 2026😎😎 От этого data race не спасает даже race detector Продолжение к предыдущему посту. Ита…
  6. Mar 4, 2026😢😢 От этого data race не спасает даже race detector Data race это частный случай race co…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →