SOER_CODE Telegram 10
TurboFan: анализ оптимизаций в V8 с помощью ассемблера (часть 1)

Чем глубже мы узнаем языки программирования, тем больше начинаем любить и ценить ассемблер. Сегодня покажу, как знание ассемблера помогает в изучении возможностей движка V8.

V8 — это движок для работы с языком JavaScript, он используется в NodeJS и браузере Chrome. Одна из особенностей этого движка — представление «горячего» кода в виде оптимизированных машинных инструкций. Для этого в движок интегрирован оптимизирующий JIT-компилятор TurboFan.

Основные задачи, которые решает TurboFan:

💡 Анализ и оптимизация «горячих» функций (которые вызываются часто);
💡 Специализация кода под конкретные типы данных;
💡 Удаление избыточных операций;
💡 Векторизация вычислений;
💡 Инлайнинг функций.

Для работы с машинным кодом традиционно используется ассемблер, поэтому давайте разберем небольшой пример, который позволит лучше понять, как работает TurboFan. В этой статье будет общее описание блоков кода, а в будущих статьях разберем детали каждого из них.

Основные моменты, которые нужны для старта:

1️⃣ Мы будем использовать nodejs для получения машинного кода функции, для этого нужно запомнить следующий шаблон команды:


node --print-opt-code --code-comments -allow-natives-syntax your_script.js


2️⃣Мы будем использовать специальную инструкцию %OptimizeFunctionOnNextCall, без нее ничего не получится.

Давайте сделаем простой скрипт:


// sum.js
function sum(a, b) {
return a + b;
}

// Прогреваем функцию (вызываем много раз, чтобы V8 её оптимизировал)
for (let i = 0; i < 10000; i++) {
sum(i, i + 1); // используем целые числа, чтобы TurboFan сделал оптимизацию именно под них
}

// Явно просим V8 оптимизировать функцию (требует --allow-natives-syntax) иначе в выводе не будет описане функции
%OptimizeFunctionOnNextCall(sum);
// Вызываем ещё раз (теперь с оптимизацией)
sum(1, 2);


Теперь запустим скрипт node --print-opt-code --code-comments -allow-natives-syntax sum.js, если мы сделали всё правильно, то получим огромный вывод на экран, из которого интересна вот эта часть:



--- Raw source ---
(a, b) {
return a + b;
}

--- Optimized code ---
optimization_id = 1
source_position = 22
kind = TURBOFAN
name = sum
stack_slots = 6
compiler = turbofan
address = 0x2edae09247a1

Instructions (size = 184)

; загрузка hidden класса объекта (проверка структуры)
0x1097064c0 0 488b59f8 REX.W movq rbx, [rcx-0x8]

; проверка контекста
0x1097064c4 4 f6433501 testb [rbx+0x35],0x1
0x1097064c8 8 0f85f2e03dfb jnz 0x104ae45c0 (CompileLazyDeoptimizedCode) ;; деоптимизация

; пролог
0x1097064ce e 55 push rbp
0x1097064cf f 4889e5 REX.W movq rbp, rsp
0x1097064d2 12 56 push rsi
0x1097064d3 13 57 push rdi
0x1097064d4 14 50 push rax

; выравнивание стека и проверка лимитов
0x1097064d5 15 4883ec08 REX.W subq rsp,0x8
0x1097064d9 19 488975e0 REX.W movq [rbp-0x20],rsi
0x1097064dd 1d 493b65a0 REX.W cmpq rsp, [r13-0x60] (external value (StackGuard::address_of_jslimit()))
0x1097064e1 21 0f8653000000 jna 0x10970653a <+0x7a> ; если стек переполнен, переходим

; ====== Основная функция ======

; Загрузка аргумента "a"
0x1097064e7 27 488b5518 REX.W movq rdx, [rbp+0x18]
0x1097064eb 2b f6c201 testb rdx,0x1
0x1097064ee 2e 0f8572000000 jnz 0x109706566 <+0xa6>

; Загрузка аргумента "b"
0x1097064f4 34 488b4d20 REX.W movq rcx, [rbp+0x20]
0x1097064f8 38 48c1f920 REX.W sarq rcx, 32 ; сразу распаковка SMI для "b"

; Подготовка аргументов к сложению
0x1097064fc 3c 488bfa REX.W movq rdi,rdx
0x1097064ff 3f 48c1ff20 REX.W sarq rdi, 32 ; распаковка для "a"
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🤡1



tgoop.com/soer_code/10
Create:
Last Update:

TurboFan: анализ оптимизаций в V8 с помощью ассемблера (часть 1)

Чем глубже мы узнаем языки программирования, тем больше начинаем любить и ценить ассемблер. Сегодня покажу, как знание ассемблера помогает в изучении возможностей движка V8.

V8 — это движок для работы с языком JavaScript, он используется в NodeJS и браузере Chrome. Одна из особенностей этого движка — представление «горячего» кода в виде оптимизированных машинных инструкций. Для этого в движок интегрирован оптимизирующий JIT-компилятор TurboFan.

Основные задачи, которые решает TurboFan:

💡 Анализ и оптимизация «горячих» функций (которые вызываются часто);
💡 Специализация кода под конкретные типы данных;
💡 Удаление избыточных операций;
💡 Векторизация вычислений;
💡 Инлайнинг функций.

Для работы с машинным кодом традиционно используется ассемблер, поэтому давайте разберем небольшой пример, который позволит лучше понять, как работает TurboFan. В этой статье будет общее описание блоков кода, а в будущих статьях разберем детали каждого из них.

Основные моменты, которые нужны для старта:

1️⃣ Мы будем использовать nodejs для получения машинного кода функции, для этого нужно запомнить следующий шаблон команды:


node --print-opt-code --code-comments -allow-natives-syntax your_script.js


2️⃣Мы будем использовать специальную инструкцию %OptimizeFunctionOnNextCall, без нее ничего не получится.

Давайте сделаем простой скрипт:


// sum.js
function sum(a, b) {
return a + b;
}

// Прогреваем функцию (вызываем много раз, чтобы V8 её оптимизировал)
for (let i = 0; i < 10000; i++) {
sum(i, i + 1); // используем целые числа, чтобы TurboFan сделал оптимизацию именно под них
}

// Явно просим V8 оптимизировать функцию (требует --allow-natives-syntax) иначе в выводе не будет описане функции
%OptimizeFunctionOnNextCall(sum);
// Вызываем ещё раз (теперь с оптимизацией)
sum(1, 2);


Теперь запустим скрипт node --print-opt-code --code-comments -allow-natives-syntax sum.js, если мы сделали всё правильно, то получим огромный вывод на экран, из которого интересна вот эта часть:



--- Raw source ---
(a, b) {
return a + b;
}

--- Optimized code ---
optimization_id = 1
source_position = 22
kind = TURBOFAN
name = sum
stack_slots = 6
compiler = turbofan
address = 0x2edae09247a1

Instructions (size = 184)

; загрузка hidden класса объекта (проверка структуры)
0x1097064c0 0 488b59f8 REX.W movq rbx, [rcx-0x8]

; проверка контекста
0x1097064c4 4 f6433501 testb [rbx+0x35],0x1
0x1097064c8 8 0f85f2e03dfb jnz 0x104ae45c0 (CompileLazyDeoptimizedCode) ;; деоптимизация

; пролог
0x1097064ce e 55 push rbp
0x1097064cf f 4889e5 REX.W movq rbp, rsp
0x1097064d2 12 56 push rsi
0x1097064d3 13 57 push rdi
0x1097064d4 14 50 push rax

; выравнивание стека и проверка лимитов
0x1097064d5 15 4883ec08 REX.W subq rsp,0x8
0x1097064d9 19 488975e0 REX.W movq [rbp-0x20],rsi
0x1097064dd 1d 493b65a0 REX.W cmpq rsp, [r13-0x60] (external value (StackGuard::address_of_jslimit()))
0x1097064e1 21 0f8653000000 jna 0x10970653a <+0x7a> ; если стек переполнен, переходим

; ====== Основная функция ======

; Загрузка аргумента "a"
0x1097064e7 27 488b5518 REX.W movq rdx, [rbp+0x18]
0x1097064eb 2b f6c201 testb rdx,0x1
0x1097064ee 2e 0f8572000000 jnz 0x109706566 <+0xa6>

; Загрузка аргумента "b"
0x1097064f4 34 488b4d20 REX.W movq rcx, [rbp+0x20]
0x1097064f8 38 48c1f920 REX.W sarq rcx, 32 ; сразу распаковка SMI для "b"

; Подготовка аргументов к сложению
0x1097064fc 3c 488bfa REX.W movq rdi,rdx
0x1097064ff 3f 48c1ff20 REX.W sarq rdi, 32 ; распаковка для "a"

BY S0ER.Code


Share with your friend now:
tgoop.com/soer_code/10

View MORE
Open in Telegram


Telegram News

Date: |

The visual aspect of channels is very critical. In fact, design is the first thing that a potential subscriber pays attention to, even though unconsciously. Each account can create up to 10 public channels The main design elements of your Telegram channel include a name, bio (brief description), and avatar. Your bio should be: Healing through screaming therapy Public channels are public to the internet, regardless of whether or not they are subscribed. A public channel is displayed in search results and has a short address (link).
from us


Telegram S0ER.Code
FROM American