Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 Контент вільний від AI. Цей пост на 100% написаний людиною, як і все на моєму блозі. Насолоджуйтесь!

18.08.2024

Словники в Go: безплатних сніданків не існує

В продовження історії з модулем unique для Go 1.23, хотів на практиці перевірити різницю між використанням символу та рядка.

Вимірював витрати памʼяті map[unique.Handle[string]]int проти map[string]int. Щоб ключ повторювався, наробив JSON з мільйоном обʼєктів {"foo": 123}. Дійсно побачив різницю: 204 Мб проти 289 Мб… тобто 85 байтів на економії на елемент. По-перше, 85 байтів це якось незрівнянно багато. По друге… чому взагалі той масив займає в памʼяті 200 Мб? Це у два рази більше, ніж текст JSON.

Почав дивитися, як зміняться витрати, якщо зробити більш ніж один ключ. А ніяк! Виявилося, що Go резервує в новому словнику місце під 8 записів. Тоді вже ясніше: насправді надлишок на ключ-рядок проти ключа-вказівника лише 11 байтів: вказівник займає в памʼяті 8 байтів, а рядок - 16 байтів, плюс 3 байти на сам зміст. (Тобто короткі рядки теж неочікувано неефективні.)

Що, взагалі, меркне в порівнянні з базовими витратами у 200 байтів на кожний малесенький словничок. Мораль історії така: низькорівнева мова не робить програму чарівно швидкою та ефективною. Роздуті структури даних та вкладені цикли можна писати будь-якою мовою.

Звісно, в Go можна зробити набагато краще: оголосити структуру. Скільки займає struct { Foo int }? 8 байтів. Але я не завжди звертав увагу на місця, де словники можна було замінити структурами. Особливо важко, коли паралельно програмуєш на мові, де між ними практично немає різниці. Таке.