Node.js на стероидах.pdf
12.7 MB
Вчера выступила с докладом про нативные модули (или native addons) в Node.js. Это расширения, написанные на C/C++, которые обеспечивают доступ к высокопроизводительным или низкоуровневым функциям, недоступным на чистом JavaScript.
По традиции, делюсь ссылкой на полезные материалы и приклыдываю свои слайды:
👉 Материалы на github
(в презентации много графиков с бенчмарками, а в материалах исходный код для них)
Если у вас есть вопросы по этой теме — пишите мне в личку @startpoint_forl, в сообщения этого канала или в комментарии под этим постом.
Также можете просто поставить реакцию, мне будет очень приятно)
По традиции, делюсь ссылкой на полезные материалы и приклыдываю свои слайды:
👉 Материалы на github
(в презентации много графиков с бенчмарками, а в материалах исходный код для них)
Если у вас есть вопросы по этой теме — пишите мне в личку @startpoint_forl, в сообщения этого канала или в комментарии под этим постом.
Также можете просто поставить реакцию, мне будет очень приятно)
❤26🔥13
А теперь — пост про то, что не влезло в доклад о нативных модулях: связь Node.js и C++, обработку исключений внутри модулей, команды для компиляции и работу с памятью.
Ещё немного про нативные модули в Node.js
Ну а я уже думаю над темами следующих интересных статей и докладов для вас. Будем копать глубже!)
Ещё немного про нативные модули в Node.js
Ну а я уже думаю над темами следующих интересных статей и докладов для вас. Будем копать глубже!)
Telegraph
Ещё немного про нативные модули в Node.js
Введение На прошлой неделе я выступала с докладом о нативных модулях Node.js — как они устроены, зачем нужны и в каких случаях стоит использовать C++ рядом с JavaScript, а в каких нет. Материалы доклада можно посмотреть в этом посте. А здесь мне хочется чуть…
🔥5❤2
В одной из частей цикла про V8 я рассказывала, почему важно сохранять массивы однородными и без «дыр» — тогда движок может их эффективнее оптимизировать.
В блоге V8 есть отличная статья, где показано, как можно посмотреть, какой тип элементов сейчас у массива и от чего он меняется. Ниже — краткий гайд, как повторить это у себя локально.
Нам понадобится отладочная (debug) сборка V8 — с ней можно смотреть не только на типы элементов, но и, например, как движок оптимизирует или деоптимизирует код.
1. Сначала ставим depot_tools, чтобы получить утилиту
2. Потом по инструкции из документации подтягиваем исходники V8.
На macOS важно: если у вас установлены только XCode Command Line Tools, их нужно удалить и поставить полноценный XCode. Подробности — здесь.
3. Дальше собираем движок:
У меня сборка шла очень долго, поэтому лучше сразу собирать правильную версию (release или debug). Debug-версия обладает бОльшими возможностями для логирования разной информации.
После сборки можно запустить движок в REPL-режиме:
Флаг
Пример вывода:
Можно и просто передать файл с кодом:
А если добавить флаг
Результат:
Дальше можно экспериментировать: добавлять в массив
В блоге V8 есть отличная статья, где показано, как можно посмотреть, какой тип элементов сейчас у массива и от чего он меняется. Ниже — краткий гайд, как повторить это у себя локально.
Нам понадобится отладочная (debug) сборка V8 — с ней можно смотреть не только на типы элементов, но и, например, как движок оптимизирует или деоптимизирует код.
1. Сначала ставим depot_tools, чтобы получить утилиту
gclient.2. Потом по инструкции из документации подтягиваем исходники V8.
На macOS важно: если у вас установлены только XCode Command Line Tools, их нужно удалить и поставить полноценный XCode. Подробности — здесь.
3. Дальше собираем движок:
gclient sync
cd /path/to/v8
git pull && gclient sync
tools/dev/gm.py arm64.debug # debug-сборка для arm на macOS
У меня сборка шла очень долго, поэтому лучше сразу собирать правильную версию (release или debug). Debug-версия обладает бОльшими возможностями для логирования разной информации.
После сборки можно запустить движок в REPL-режиме:
v8/out/arm64.debug/d8 --allow-natives-syntax
Флаг
--allow-natives-syntax позволяет использовать специальные отладочные функции, такие как %DebugPrint(array):
d8> const array = [1, 2, 3]; %DebugPrint(array);
Пример вывода:
DebugPrint: 0x2dcc00389c0d: [JSArray]
- map: 0x2dcc0005b7d1 <Map[16](PACKED_SMI_ELEMENTS)> [FastProperties]
- prototype: 0x2dcc0005b7f9 <JSArray[0]>
- elements: 0x2dcc0006ca25 <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
- length: 3
- properties: 0x2dcc000007bd <FixedArray[0]>
.....
Можно и просто передать файл с кодом:
v8/out/arm64.debug/d8 --allow-natives-syntax ~/Documents/all-examples/v8/test.js
А если добавить флаг
--trace-elements-transitions, то движок будет печатать все изменения типа:
// test2.js
const array = [1, 2, 3];
array[3] = 4.56;
v8/out/arm64.debug/d8 --trace-elements-transitions ~/Documents/all-examples/v8/test2.js
Результат:
elements transition [PACKED_SMI_ELEMENTS -> PACKED_DOUBLE_ELEMENTS] in ~+15 at test2.js:1 for 0x2cf600389c45 <JSArray[3]> from 0x2cf60006ca0d <FixedArray[3]> to 0x2cf600389c55 <FixedDoubleArray[22]>
Дальше можно экспериментировать: добавлять в массив
undefined, -0, пропущенные индексы — и смотреть, как V8 сразу меняет внутренний тип. Это наглядный способ понять, как движок анализирует наш код и почему иногда одно лишнее значение или неправильная инициализация может замедлить выполнение.👍11🔥5
Как можно профилировать память в Node.js?
Один из базовых способов — heap snapshot через Chrome DevTools. Для этого нужно запустить Node с флагом --inspect:
После запуска можно открыть в браузере
Если же мы хотим отслеживать метрики памяти в реальном времени, то можно сделать это с помощью PerformanceObserver. Он слушает события, создаваемые системой perf_hooks. Например, можно смотреть, как растёт heap и когда происходят сборки мусора:
Если нужно больше деталей о работе V8 и нет времени писать лишний код, можно просто запустить процесс с флагом --trace_gc:
В консоли появятся строки вроде:
Здесь видно тип сборки мусора, её длительность и изменение размера кучи.
Ну а самый простой и быстрый способ получить обзор текущего состояния, и, скорей всего, вы про него уже не раз слышали — метод process.memoryUsage. Он возвращает данные по основным сегментам памяти:
Один из базовых способов — heap snapshot через Chrome DevTools. Для этого нужно запустить Node с флагом --inspect:
node --inspect app.js.После запуска можно открыть в браузере
chrome://inspect и подключиться к процессу в DevTools и на вкладке Memory снять снимок кучи. Он показывает, какие объекты занимают память.Если же мы хотим отслеживать метрики памяти в реальном времени, то можно сделать это с помощью PerformanceObserver. Он слушает события, создаваемые системой perf_hooks. Например, можно смотреть, как растёт heap и когда происходят сборки мусора:
import { PerformanceObserver, performance } from 'node:perf_hooks';
const obs = new PerformanceObserver((items) => {
for (const entry of items.getEntries()) {
console.log(`[${entry.entryType}]`, entry.name, entry.duration.toFixed(2), 'ms');
}
});
obs.observe({ entryTypes: ['gc'] });
Если нужно больше деталей о работе V8 и нет времени писать лишний код, можно просто запустить процесс с флагом --trace_gc:
node --trace_gc app.js.В консоли появятся строки вроде:
[89452:0xa81400000] 3103 ms: Scavenge 18.9 (27.7) -> 18.7 (50.7) MB, pooled: 0 MB, 4.29 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
Здесь видно тип сборки мусора, её длительность и изменение размера кучи.
Ну а самый простой и быстрый способ получить обзор текущего состояния, и, скорей всего, вы про него уже не раз слышали — метод process.memoryUsage. Он возвращает данные по основным сегментам памяти:
const mem = process.memoryUsage();
console.log(`Heap used: ${(mem.heapUsed / 1024 / 1024).toFixed(2)} MB`);
console.log(`RSS: ${(mem.rss / 1024 / 1024).toFixed(2)} MB\n`);
👍5❤3