четверг, 6 июня 2019 г.

V8 internals - хранение объектов и массивов. Инициализация глобального контекста

       Итак, я продолжаю свои упражнения в археологии. Для начала меня заинтересовал вопрос - вот есть у меня виртуальная машина, а как в ней появляются все эти прекрасные "встроенные функции" ? Собственно в этой статье это довольно подробно объясняется - https://v8.dev/blog/custom-startup-snapshots  Если в кратце то  в папочке tools есть утилита js2c.py которая конвертирует javascript код из папочки lib в бинарный массив который можно напрямую загрузить в V8 Heap.  Таким образом инициализация контекста (объект global) очень сильно ускоряется.
        Что касается хранения объектов в памяти V8 (memory layout) - по этой теме есть много статей, и даже доклад от Fedor Indutnyhttps://www.youtube.com/watch?v=tLyIs_0cUyc но на этот раз меня его доклад не впечатлил. Он прошелся по верхушкам и прорекламировал llnode - node.js плагин для lldb. Большое спасибо Федору за инструмент, но тема memory layout в его докладе была раскрыта на 10% не больше. Лучше всего это объяснено в этой статье - https://v8.dev/blog/fast-properties В ней акцент сделан на хранении свойств, а в следующей статье https://v8.dev/blog/elements-kinds более подробно объяснено хранение элементов массива.   Вообще в статьях очень много полезной информации по оптимизации - как писать код так чтобы V8 его быстрее исполнял. Ведь благодаря создателям JavaScript - одну и ту же вещь там можно написать 20 способами. Вообще мне кажется что люди проектировавшие JavaScript изначально - прямо-таки ненавидили разработчиков компиляторов/интерпретаторов и сделали все чтобы затруднить разработку компиляторов/интерпретаторов для JavaScript. 
В общем обе статьи я бы рекомендовал прочитать каждому JS разработчику стремящемуся писать оптимальный код. Для тех кто не в ладах с английским языком перескажу в кратце:
  • Если вы хотите чтобы доступ к свойствам объекта происходил быстро - явно определяйте все его свойства при создании объекта. Определяйте их в одном и том же порядке(в конструкторе). Не добавляйте новые свойства по ходу использования объекта. Не используйте Object.defineProperty() если у вас нет для этого большой необходимости. 
  • Избегайте полиморфизма. То есть передайвайте в функцию только аргументы одного типа. 
  • Не храните элементы разных типов в одном массиве. Явно приводите их к одному виду.
  • Избегайте разряженных массивов (массивов с пропусками). Сплошные массивы работают намного быстрее
  • Не пытайтесь обращаться к несуществующим элементам массива (требует обращения к prototype)

среда, 5 июня 2019 г.

V8 internals/Кишки V8

       Продолжаю разбираться как внутри устроен V8. Из интересного - доклад про Ignition - интерпретатор JS который V8 использует при первом исполнении JS кода:

При многократном выполнении код компилируется оптимизирующим компилятором TurboFan (с ним пока не разбирался).
    Начал копать memory layout по исходникам. Начал с src/objects.h - там общая иерархия объектов описана.  Если в кратце то V8 имеет следующие типы объектов(OBJECT_TYPE_LIST):

  • Smi - immediate small integer 
  • LayoutDescriptor
  • HeapObject - superclass for everything allocated in the heap
  • Primitive
  • Number
  • Numeric
В V8 используется хитрая схема тэгирования указателей (tagging scheme) -  так как все что они читают/пишут из памяти выровненно по границе машинного слова (4/8 байт) то младшие биты указателя всегда должны быть равны нулю. Они используют их для тэгирования значения хранящегося в области памяти. Если младший бит ноль - то это вовсе не указатель а Smi -целочисленное значение хранящиеся в верхних 31 бите указателя. Если младший бит равен 1 - то это указатель на HeapObject. Второй с конца бит используется для тэгирования WeakObject. Если второй бит равен 1 то это указатель на WeakHeapObject. 
Реализация лежит в include/v8-internal.h
 В добавок ко всему этому если архитектура 64-битная то Node.js умеет сжимать указатели (также как и Java Runtime).  
       Из источников информации отдельно хочется отметить https://github.com/danbev/learning-v8 Автор провел просто фантастическую работу по разбору кишков V8 с отладчиком в руках. И не поленился все это задокументировать. Вообще сама по себе идея документировать то что ты исследуешь в github очень крутая. У этого же автора еще штук пять подобных репозиториев где он разбирает libuv, node.js и прочее. 
      Еще для понимания очень полезным оказался V8’s Object Model Using Well-Defined C++  

понедельник, 3 июня 2019 г.

Этот день мы приближали как могли...

    Этот пост я пишу из поезда Ульяновск-Москва. Мало кто знает но я как минимум раньше очень часто ездил на поездах. Практически каждые выходные катал из Москвы в Ульяновск и обратно. Это продолжалось долгие семь лет (с перерывом на полтора года Вьетнама). И все это время я думал - когда же в поезде появится интернет ??? Так вот в купейных вагонах ТКС он наконец-то появился. Бесплатный. Я практически уверен что на просмотр фильма его не хватит - но для серфинга более чем достаточно. Я не знаю каких усилий потребовала реализация подобного проекта в условиях бюрократии РЖД - но я думаю не малых. В общем респект тем кто это сделал. А то что это еще и бесплатно - это в двойне приятно!

четверг, 30 мая 2019 г.

Внутренности Node.js

            Не могу не поделится классным одним докладом по внутренностям Node.js:
Судя по акценту парень говорит на русском лучше чем на английском. Слайды можно посмотреть http://jsconfbp.indutny.com/#/ 
Не смотря что доклад 4-х летней давности, основные моменты он раскрывает. Все ObjectWrap, AsyncWrap, TCPConnectWrap до сих пор используются в коде ноды. Также раскрывается то как инициализируются встроенные модули написанные на плюсах. 
Ну и напоследок не могу не похвастаться - мой первый пул реквест в node.js попал в мастер - https://github.com/nodejs/node/pull/27933 
Второй на рассмотрении https://github.com/nodejs/node/pull/27978 

среда, 29 мая 2019 г.

Мониторинг Node.js

      В последнее время озадачился мониторингом Node.js приложений. И понял что там все довольно печально. Начал контрибьютить в несколько довольно известных npm модулей дабы исправить эту ситуацию. Пока дела обстоят с переменным успехом:

  • Удалось добавить статистики в node-gcstats https://github.com/dainis/node-gcstats/pull/38#issuecomment-496421764 Научил  этот NPM модуль возвращать number_of_native_contexts() / number_of_detached_contexts().  В нормальном приложении number_of_detached_contexts() должно быть равно 0. Если в вашем приложении это не так - у вас большие проблемы с утечкой памяти.
  • Не удалось пропихнуть пул реквест в Node.js Prometheus клиент - к сожалению не могу убедить автора что breaking changes это плохо и лучше их избегать - https://github.com/siimon/prom-client/pull/260 В общем если кто-то читает этот блог - плюсаните пожалуйста pull реквест, может это сподвигнет автора принять его.

вторник, 28 мая 2019 г.

Node.js

          Последние лет 5 я был очень погружен в бэкэнд и инфраструктуру и все что происходило с фронтенд технологиями обходило меня стороной. В первую очередь Node.js. Сейчас у меня есть чуть больше чем дохрена свободного времени и я решил разобраться что же это такое. 
          Если совсем просто - взяли библиотеку под названием V8 которая может выполнять JS код. Прикрутили к ней другую библиотеку - libuv,  добавили немного обвязки по вкусу:
Если сравнить с  runtime других языков программирования - мягко говоря не густо. Но на мой взгляд в этой простоте - единственное достоинство ноды. Ну и еще npm - огонь. Все остальное я бы записал в недостатки. 
         Главный из них - однопоточность ноды. Нода однопоточна потому что V8 создавался как движок для браузера, а в браузере весь JS исполняется в одном потоке. И на клиенте это всех в принципе устраивает.  Но для server-side это на мой взгляд это большой недостаток. Львиная часть всех статей про производительность ноды повторяет одну фразу - не занимайте event loop.  Не выполняйте никаких долгих операций, не загружайте большие JSON. Только представьте, Node.js которая создана для  JS не может кушать большие куски JSON. JS не может осилить JSON, Карл! В общем это второе пришествие корпоративной многозадачности для тех кто не успел по программировать для Windows 3.11
         Второй большой недостаток ноды - это JavaScript. Да, вы не ослышались. JavaScript не плохо подходит для написание небольших кусков кода для клиента, где сборщик мусора по большому счету не нужен. Но когда это все крутится на backend - сборка мусора и ее скорость становится довольно критичной. В V8 реализовали очень приличный сборщик мусора, конкурентный mark and sweep, с поддержкой поколений и отдельной кучей для больших объектов - https://v8.dev/blog/trash-talk Но если на объект остаются ссылки - то даже самый лучше сборщик мусора не соберет этот объект. 
            Структура языка JavaScript провоцирует создание утечек памяти. Куча колбеков и анонимных функций. Каждый объект замыкания - это ссылка на все переменные находящиеся в области видимости. Замыканий много, переменный находящиеся в области видимости далеко не всегда прописываются в коде явным образом. Поэтому отследить образование нежелательных ссылок на объекты очень трудно.

четверг, 23 мая 2019 г.

Базовое знание OS и сетевых протоколов

        Практически на всех своих собеседованиях я задаю простой вопрос: Представь я открываю браузер и ввожу в адресную строку www.ozon.travel и нажимаю enter. Что при этом происходит?     
      Этот вопрос с одной стороны простой, с другой стороны - он позволяет быстро понять насколько человек знает сразу две области - работу операционных систем и работу сетевых протоколов. Для разных позиций я требую разную глубину понимания этого процесса. 
         Для ручного тестировщика достаточно просто знания что есть dns, есть http, есть tcp и ip и что они вместе работают как-то так. 
       Для разработчика/тестировщика-автоматизатора обязательно знание того как просиходит резолвинг имен, как происходит установление tcp коннекта и 100% понимание того как работает HTTP. 
       Для админа добавляется знание о том что есть системные вызовы, есть процессы, основные настройки влияющие на прохождение этого flow, базовые понятия о маршрутизации. Какие возможны сценарии закрытия TCP соединения, TCP slow start, TCP congestion control, и тд.
       Долгое время я считал что какого-то одного источника, который все это сразу сможет объяснить - не существует. Но не давно наткнулся на лекцию своего бывшего коллеги Владимира Иванова, которую тот давал еще в Яндексе - https://events.yandex.ru/lib/talks/2336/
Всем кто не смотрел - всячески рекомендую.  Он очень простыми и понятными словами доносит весьма сложные для понимания вещи - начиная от работы системных вызовов и заканчивая работой протоколов маршрутизации.