среда, 23 января 2013 г.

Внутреннее устройство Go

     Недавно прочитал две очень интересные статьи про то как в Go реализованы различные типы данных:
Если уровень английского позволяет, лучше прочитать их в оригинале - если нет, то я перескажу в кратце:
1. Все простые типы данных - int, float и тд занимают ровно столько же места сколько они занимают в Си. 
2. Массив в Go - это тоже самое что и массив в Си - никаких накладных расходов. То есть массив из 10 значений типа int будет занимать в памяти ровно 4 * 10 = 40 байт. И не байта больше.
3. То же самое относится и к структурам - размер памяти требуемый для размещения структуры - это сумма размеров ее членов и не байта больше.
4. Строки в Go тоже устроены весьма минималистично. Строка представляет собой структуру из двух значений размером в слово - в первом значении хранится длинна строки, а во втором слове - указатель на массив байт в котором фактически хранится строка. Примерно также строки хранятся практически во всех реализациях этого типа на C++.
5. Слайс представляет собой структуру из 3-х слов - указатель на массив в котором хранятся данные, длинну слайса и  вместительность (capacity) слайса. При создании слайса не происходит никакого копирования - просто появляется еще один указатель на тот же самый массив в котором хранятся данные самой строки, из которой создается слайс. 
6. Интерфейс - это на самом деле не то же самое что и структура, из которой был создан этот интерфейс. То есть на самом деле когда мы приводим экземпляр типа к какому-то интерфейсу получается совершенно новое значение - экземпляр интерфейса. Экземпляр интерфейса  представляет  собой структуру из двух указателей  - указатель на тот объект который фактически скрывается за интерфейсом и указательна таблицу, помогающую транслировать обращение к методам объявленным в интерфейсе к фактическим методам объявленным в объекте, от которого образован данный интерфейс. Это объясняет почему когда нужно передать в какую нибуть функцию объект произвольного типа, его объявляют просто как interface{} а не как указатель на интерфейс. Потому что сам интерфейс занимает не больше 2 машинных слов.

Если честно я восхищен насколько эффективно Go работает с памятью. Накладные расходы находятся на уровне С++ - и при всем этом Go полностью берет на себя управление памятью, избавляя программистов от знания всех тонкостей работы всяких "умных указателей",  которые при неграмотном применении плодят очень трудно уловимые баги. Это дает программисту возможность не замораиватся подсчетом ссылок а заниматся реализацией поставленной задачи.

PS
Для сравнения PHP для размешения 1 мегабайта данных в массиве требуется почти в 10 раз больше памяти.