awesome-everything EN
↑ Обратно к восхождению

Браузер и фронтенд-рантайм

Внутренности V8: чтение кода и трасс

Суть Читайте реальные JS-сниппеты и строку deopt-трассы, предсказывайте поведение V8 — поломку hidden class, дрейф monomorphic→megamorphic, триггеры deopt — и выбирайте самый эффективный фикс.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги производительности V8 прячутся в обычном на вид коде. Прочитайте каждый сниппет и строку трассы, предскажите, что V8 делает с hidden class, IC или оптимизированным уровнем — затем выберите фикс, который сеньор делает первым, до того как трогать флаг.

Цель

Отработайте цикл, который вы запускаете в каждом инциденте V8: прочитать горячий путь, предсказать, откуда берётся нестабильность формы или типа, и взять фикс выше по потоку, который держит сайт вызова monomorphic, а значения — стабильными по типу.

Сниппет 1 — позднее свойство

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

function build(coords) {
  const out = [];
  for (const c of coords) {
    const p = new Point(c.x, c.y);
    if (c.label) p.label = c.label;   // только иногда
    out.push(p);
  }
  return out;
}

// позже, в горячем цикле:
function sumX(points) {
  let s = 0;
  for (const p of points) s += p.x;   // горячий IC-сайт
  return s;
}
Викторина

Некоторым точкам .label добавляется после конструктора, некоторым — нет. Что происходит на сайте доступа p.x в sumX и каков фикс?

Сниппет 2 — ловушка dictionary mode

function makeConfig(overrides) {
  const cfg = { host: 'localhost', port: 8080, retries: 3 };
  for (const key of Object.keys(overrides)) {
    cfg[key] = overrides[key];
  }
  delete cfg.retries;          // "почистить" неиспользуемый дефолт
  return cfg;
}

function readPort(cfg) {
  return cfg.port;             // горячий путь, вызывается на запрос
}
Викторина

readPort находится на горячем пути запроса. Что делает с ним delete в makeConfig и каков фикс?

Сниппет 3 — переполнение Smi

function checksum(ids) {
  let acc = 0;
  for (let i = 0; i < ids.length; i++) {
    acc = (acc * 31 + ids[i]);     // быстро растёт на больших массивах
  }
  return acc;
}
Викторина

checksum оптимизирован TurboFan и быстр на малых входах, но на больших массивах внезапно делает deopt и замедляется. В чём причина на уровне V8 и каков фикс?

Сниппет 4 — трасса deopt

[marking 0x... <JSFunction processItem> for optimization using TurboFan]
[deoptimizing (DEOPT eager): begin <JSFunction processItem> (opt #42) @14, ;;; deoptimize at <main.js:42:18>, wrong map]
[marking 0x... <JSFunction processItem> for optimization using TurboFan]
[deoptimizing (DEOPT eager): begin <JSFunction processItem> (opt #43) @14, ;;; deoptimize at <main.js:42:18>, wrong map]
[marking 0x... <JSFunction processItem> for optimization using TurboFan]
[deoptimizing (DEOPT eager): begin <JSFunction processItem> (opt #44) @14, ;;; deoptimize at <main.js:42:18>, wrong map]
Викторина

Читая этот вывод --trace-deopt, какое утверждение верно?

Итог

Любой инцидент V8 читается одинаково в коде и трассах: условно добавленное свойство делит один массив на два hidden class и толкает горячий сайт в polymorphic; delete опрокидывает объект в постоянный dictionary mode; аккумулятор, переросший диапазон Smi, боксируется в HeapNumber и делает deopt цикла TurboFan; а повторяющийся ‘wrong map’ deopt на одной позиции — сигнатура deopt-loop. Диагностируйте по трассе, затем чините выше по потоку в модели данных — сведите форму к одной, уберите delete, ограничьте числовой диапазон — прежде чем браться за флаг.

Продолжить восхождение ↑Внутренности V8: охота на deopt и megamorphic IC
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.