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

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

02.03.2023

Порівняння витрат памʼяті Ruby та Go на масиви

Хотів написати пост про те, як Go краще витрачає памʼять, ніж Ruby, та ще й з прикладами. Але вийшла несподіванка — не такий вже Ruby й поганий! На масив чисел якимсь чином витрачає десь стільки ж памʼяті (на 2% менше навіть!), а от на масив рядків вже на 50% більше, що більш зрозуміло. Код прикладів у gist. Тут треба більше розбиратись.

По-перше, що вимірювати як витрати пам’яті. На мою думку, краще за все дивитись на загальний розмір памʼяті, виділений процесом, тобто RSS - Resident Set Size. Ані в Ruby, ані в Go немає вбудованих засобів дізнатись цей розмір, але його можна легко отримати командою оболонки ps -o rss $PID_OF_PROGRAM. Корисно, що цей показник не спирається на особливості мови чи платформи, тому таке порівняння більш справедливе. Залишається тільки виділяти достатньо памʼяті. Та про всяк випадок викликати збирач сміття перед вимірюваннями; хоча в такому простому тесті це не має ніякого ефекту. Збирати сміття варто, якщо вимірювати витрати цілого алгоритму.

Так от, виходить, що як Go, так і Ruby зберігають масив цілих чисел у 64-бітному відображенні, та практично без додаткових витрат. Причому в Go кількість бітів у типі задається явно, а от від Ruby можна було очікувати й 32-бітних чисел, бо мої значення влазять в 32 біти. Втім, виглядає так, що Ruby завжди використовує 64-бітні числа, що логічно на моїй 64-бітній системі.

Рядки на Go витрачають, окрім байта на кожний символ, додатково 8 байтів на вказівник, та 8 байтів на довжину рядка (або так це виглядає). В Ruby, напевно, рядок містить ще якесь значення — можливо, кодування.

Можемо також підтвердити економію, яку Go отримує на тому, що рядки — є константи]. А саме, якщо масив наповнити не різними рядками, а одним й тим самим рядком, то витрати памʼяті будуть такі самі, як на масив чисел. І це незалежно від довжини цього рядка. Це пояснюється тим, що фактично масив містить вказівники на один та той самий рядок, а вказівник займає ті самі 8 байтів, як і число. В Ruby можна досягнути такої самої економії, якщо робити масив символів, тобто :symbol. Що зайвий раз підкреслює важливість використання символів там, де одне й те саме значення повторюватиметься багато разів — наприклад, при декодуванні JSON.

Але масиви — то надто просто. Цікавіше буде порівняти витрати на структури або хеші — фундамент сучасного програмування. Про це — наступного разу.