Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CodeFest 2018. Юлия Маллаева и Денис Былинин (Яндекс) — Переход V8 с Crankshaft на Turbofan+Ignition

CodeFest
April 05, 2018

CodeFest 2018. Юлия Маллаева и Денис Былинин (Яндекс) — Переход V8 с Crankshaft на Turbofan+Ignition

Посмотрите выступление Юлии и Дениса: https://2018.codefest.ru/lecture/1285/

— Внутреннее устройство Turbofan и Ignition
— Что изменилось в сравнении с Crankshaft
— Как это повлияло на общую производительность
— Как это повлияло на глубокую оптимизацию
— Как теперь оптимизировать свой код, как читать и понимать новые трейсинги движка

CodeFest

April 05, 2018
Tweet

More Decks by CodeFest

Other Decks in Programming

Transcript

  1. Переход v8 с Crankshaft на Turbofan+Ignition Маллаева Юлия разработчик команды

    спецпроектов Yandex Былинин Денис разработчик команды спецпроектов Yandex
  2. О чем расскажем › История от Crankshaft до Turbofan+Ignition ›

    Внутреннее устройство Turbofan и Ignition › Что изменилось в сравнении с Crankshaft › Как это повлияло на общую производительность › Как это повлияло на глубокую оптимизацию 3
  3. Ignition: итоги › уменьшение потребляемой памяти › байткод как источник

    информации о коде › отсутствие репарсинга › кроссплатформенность › удобство генерации графа для оптимизаций Turbofan 14
  4. Turbofan: цели 16 › полная поддержка всех фич ESNext ›

    предсказуемая производительность › легкость портирования на новые платформы
  5. Sea of Nodes Представление для оптимизации - потоки данных ›

    Отслеживание мертвого кода › Изменения порядка исполнения Представление для генерации кода - потоки управления › Строгая последовательность блоков ▌ Как воспользоваться одновременно двумя? 17
  6. Turbofan: особенности Разделение высокоуровневого/низкоуровневого кода › Гибкость в добавлении новых

    платформ › Уменьшение количества платформо-специфичного кода Отслеживание статической информации о типах › asm.js & wasm 18
  7. Feedback Vector o { x: ? } o { y:

    ? } IC stubs Feedback Vectors Code o.x o.y ... 21
  8. Orinoco › Требует меньше памяти › Большой потенциал для улучшений

    › В среднем в 4 раза меньше пауза на сборки мусора, чем у предыдущего сборщика мусора 23
  9. Бонусы на практике › проще поддержка новых архитектур › проще

    внедрение ESNext фич › прирост производительности в реальных приложениях › ориентация на комьюнити, а не на пиковую производительность 26
  10. Бонусы на практике › проще поддержка новых архитектур › проще

    внедрение ESNext фич › прирост производительности в реальных приложениях › ориентация на комьюнити, а не на пиковую производительность › убийцы оптимизации и скачки производительности › бонусы для нативных модулей Node.js и основных подходов (~10%) 27
  11. Что есть производительность › неоптимизированный код - наиболее общее представление

    исходного кода › оптимизированный код - код, сгенерированный исходя из предположений о типах используемых переменных (спекулятивные оптимизации) 33
  12. Декларативность › излишние оптимизации › неясные намерения, неясная логика выхода

    из цикла › eval() › общие переменные и сайд эффекты › иррациональные вещи 35
  13. Излишние оптимизации function length (x1, y1, x2, y2) { return

    sqrt( pow(x1 - x2, 2) + pow(y1 - y2, 2) ); } class Point { constructor (x, y) { this.x = x; this.y = y; } distance (that) { return sqrt( pow(this.x - that.x, 2) + pow(this.y - that.y, 2) ); } } function length (x1, y1, x2, y2) { const start = new Point(x1, y1); const end = new Point(x2, y2); return start.distance(end); } 36
  14. Инлайн function length (x1, y1, x2, y2) { const start

    = new Point(x1, y1); const end = new Point(x2, y2); return start.distance(end); } function length (x1, y1, x2, y2) { const start = {x: x1, y: y1}; const end = {x: x2, y: y2}; return sqrt( pow(start.x - end.x, 2) + pow(start.y - end.y, 2) ); } 37
  15. Escape-анализ и Скаляризация function length (x1, y1, x2, y2) {

    const start = {x: x1, y: y1}; const end = {x: x2, y: y2}; return sqrt( pow(start.x - end.x, 2) + pow(start.y - end.y, 2) ); } function length (x1, y1, x2, y2) { const start_x = x1; const start_y = y1; const end_x = x2; const end_y = y2; return sqrt( pow(start_x - end_x, 2) + pow(start_y - end_y, 2) ); } 38
  16. Неясные намерения function foo2 (f) { switch (arguments.length) { case

    1: return f(); case 2: return f(arguments[1]); case 3: return f(arguments[1], arguments[2]); default: { var args = []; for (var i = 1; i < arguments.length; ++i) { args[i - 1] = arguments[i]; } return f.apply(undefined, args); } } } function foo1 (f, ...args) { return f(...args); } 39
  17. Проведем бенчмарк suite .add('foo1', () => { foo1(()=>{}, 1, 2,

    3, 4, 5, 6, 7);}) .add('foo2-many', () => { foo2(()=>{}, 1, 2, 3, 4, 5, 6, 7);}) .add('foo2-few', () => { foo2(()=>{}, 1, 2);}) .add('foo2-no', () => { foo2((()=>{});}) 40
  18. Проведем бенчмарк: node v7.10.0 $> node bench.js foo1 x 1,042,425

    ops/sec ±9.47% (78 runs sampled) foo2-many x 6,137,539 ops/sec ±10.51% (68 runs sampled) foo2-few x 17,039,482 ops/sec ±1.63% (85 runs sampled) foo2-no x 19,670,160 ops/sec ±1.86% (85 runs sampled) Fastest is foo2-no 41
  19. Проведем бенчмарк: node v8.9.3 $> node bench.js foo1 x 38,942,040

    ops/sec ±0.89% (82 runs sampled) foo2-many x 4,342,790 ops/sec ±0.54% (88 runs sampled) foo2-few x 6,938,418 ops/sec ±9.13% (79 runs sampled) foo2-no x 8,288,474 ops/sec ±5.93% (78 runs sampled) Fastest is foo1 42
  20. Проведем бенчмарк: node v9.8.0 $> node bench.js foo1 x 424,671,257

    ops/sec ±0.80% (85 runs sampled) foo2-many x 4,096,449 ops/sec ±0.44% (87 runs sampled) foo2-few x 7,646,297 ops/sec ±0.93% (87 runs sampled) foo2-no x 9,032,627 ops/sec ±0.94% (90 runs sampled) Fastest is foo1 43
  21. Проведем бенчмарк let c = 0; suite .add('foo1', () =>

    { foo1(()=>c++, 1, 2, 3, 4, 5, 6, 7);}) .add('foo2-many', () => { foo2(()=>c++, 1, 2, 3, 4, 5, 6, 7);}) .add('foo2-few', () => { foo2(()=>c++, 1, 2);}) .add('foo2-no', () => { foo2((()=>c++);}) 44
  22. Проведем бенчмарк: node v9.8.0 $> node bench.js foo1 x 40,599,880

    ops/sec ±0.69% (87 runs sampled) foo2-many x 4,143,526 ops/sec ±0.82% (87 runs sampled) foo2-few x 7,656,449 ops/sec ±1.16% (86 runs sampled) foo2-no x 7,429,207 ops/sec ±1.02% (87 runs sampled) Fastest is foo1 45
  23. Оптимизированный код foo1 --- Optimized code –- optimization_id = 605

    source_position = 181 kind = OPTIMIZED_FUNCTION name = foo1 stack_slots = 7 compiler = turbofan Instructions (size = 344) … 47 foo2 --- Optimized code --- optimization_id = 1480 source_position = 233 kind = OPTIMIZED_FUNCTION name = foo2 stack_slots = 10 compiler = turbofan Instructions (size = 1848) …
  24. Общие переменные и сайд-эффекты var b = false; var n

    = 0; function f() { g(); return n; } function g() { if (b) n = 42; } for (let i = 0; i < 100000; i++) { f(); } b = true; f(); 48
  25. Ленивая (lazy) деоптимизация 49 › помечает связанные функции › при

    следующем вызове передает управление в неоптимизированный код › с проверкой связано множество функций
  26. Как найти? $> node --trace_deopt lazy-deopt.js [marking dependent code 0x1dd2105479c1

    (opt #1) for deoptimization, reason: property-cell-changed] [deoptimize marked code in all contexts] [deoptimizer found activation of function: f / 69646727971] [deoptimizing (DEOPT lazy): begin 0x69646727971 <JSFunction f (sfi = 0x69646727329)> (opt #1) @0, FP to SP delta: 16, caller sp: 0x7fff513f7c40] 50
  27. Просто не надо так function NewObject (proto) { return {

    __proto__: proto }; } const obj = new NewObject(42); …??? 51
  28. Особенности V8 › предел полиморфизма/нарушения мономорфизма › циклы Garbage Collector

    › leaky arguments vs spread vs arguments to array › удаление не последнего свойства в объекте › etc. 52
  29. Что стало можно › let/const › try/catch › for ...

    of loops › generators › async › curry/bind › inlining (large comments / large names) › rest parameters › destructuring › map/reduce/forEach 53
  30. Try/catch function tryCatch(){ try { if (Math.random() > 0.5) throw

    new Error('Catch it!'); return true; } catch(err) { return false; } } 54
  31. Try/catch Now using node v6.13.1 (npm v3.10.10) $> node --trace_opt

    trycatch-opt.js | grep tryCatch [disabled optimization for 0x3c1d5007c879 <SharedFunctionInfo tryCatch>, reason: TryCatchStatement] 55
  32. Try/catch Now using node v8.10.0 (npm v5.6.0) $> node --trace_opt

    trycatch-opt.js | grep tryCatch [compiling method 0x280fcad8d699 <JSFunction tryCatch (sfi = 0x280f67464139)> using TurboFan] [optimizing 0x280fcad8d699 <JSFunction tryCatch (sfi = 0x280f67464139)> - took 1.529, 0.723, 0.017 ms] [completed optimizing 0x280fcad8d699 <JSFunction tryCatch (sfi = 0x280f67464139)>] 56
  33. Нарушение мономорфизма 57 function f(o) { return o.x; } for

    (let i = 0; i < 100000; i++) { f({ x: 9 }); } f({ y: 1, x: 9 });
  34. Предел полиморфизма function f(o) { return o.x; } for (let

    i = 0; i < 100000; i++) { f({ x: 9 }); f({ x: 9, a: 1 }); f({ x: 9, b: 2 }); f({ x: 9, c: 3 }); } f({ y: 1, x: 9 }); 58
  35. Энергичная (eager) деоптимизация › происходит сразу в случае провала предположения

    › вызов неоптимизированного кода происходит немедленно › с проверкой связана только одна функция 59
  36. Как найти? $> node --trace_deopt eager-deopt.js [deoptimizing (DEOPT eager): begin

    0xd30b7b0acf1 <JSFunction f (sfi = 0xd30fa175d31)> (opt #0) @1, FP to SP delta: 16, caller sp: 0x7fff5fbfe0f8] ;;; deoptimize at </Users/yuu-mao/tracing-flags/eager- deopt.js:2:12>, wrong map 60
  37. Ограничения физической реальности 〉32-bit integers vs integers stored in doubles

    〉неоднородный массив vs typed arrays 〉очень большие натуральные числа трансформируются в HeapNumbers 61
  38. Вывод 〉на первый план выходит проектирование и написание семантичного кода

    〉от особенностей никуда не денешься, придется знать и понимать 〉особенности стали куда более предсказуемы 〉js растет и развивается, впитывая в себя новые крутые технологии 62