Перейти к основному содержимому

Indicium: инструмент трассировки времени выполнения V8

· 7 мин. чтения
Зейнеп Джанкара ([@ZeynepCankara](https://twitter.com/ZeynepCankara))

Последние три месяца были для меня потрясающим опытом обучения, так как я присоединилась к команде V8 (Google London) в качестве стажера и работала над новым инструментом под названием Indicium.

Этот системный анализатор представляет собой унифицированный веб-интерфейс для трассировки, отладки и анализа шаблонов создания и модификации Inline Caches (ICs) и карт (Maps) в реальных приложениях.

В V8 уже существует инфраструктура трассировки для ICs и карт (Maps), которая может обрабатывать и анализировать события IC с помощью IC Explorer и события карт с помощью Map Processor. Однако предыдущие инструменты не позволяли анализировать карты и ICs комплексно, что теперь возможно с системным анализатором.

Indicium

Исследование

Давайте рассмотрим пример, чтобы продемонстрировать, как мы можем использовать Indicium для анализа событий журналов Map и IC в V8.

class Point {
constructor(x, y) {
if (x < 0 || y < 0) {
this.isNegative = true;
}
this.x = x;
this.y = y;
}

dotProduct(other) {
return this.x * other.x + this.y * other.y;
}
}

let a = new Point(1, 1);
let b = new Point(2, 2);
let dotProduct;

// разогрев
for (let i = 0; i < 10e5; i++) {
dotProduct = a.dotProduct(b);
}

console.time('snippet1');
for (let i = 0; i < 10e6; i++) {
dotProduct = a.dotProduct(b);
}
console.timeEnd('snippet1');

a = new Point(-1, -1);
b = new Point(-2, -2);
console.time('snippet2');
for (let i = 0; i < 10e6; i++) {
dotProduct = a.dotProduct(b);
}
console.timeEnd('snippet2');

Здесь у нас есть класс Point, который хранит две координаты и дополнительное булево значение на основе значений координат. У класса Point есть метод dotProduct, который возвращает скалярное произведение между переданным объектом и получателем.

Чтобы упростить объяснение программы, давайте разделим ее на два фрагмента (игнорируя фазу разогрева):

фрагмент 1

let a = new Point(1, 1);
let b = new Point(2, 2);
let dotProduct;

console.time('snippet1');
for (let i = 0; i < 10e6; i++) {
dotProduct = a.dotProduct(b);
}
console.timeEnd('snippet1');

фрагмент 2

a = new Point(-1, -1);
b = new Point(-2, -2);
console.time('snippet2');
for (let i = 0; i < 10e6; i++) {
dotProduct = a.dotProduct(b);
}
console.timeEnd('snippet2');

После выполнения программы мы замечаем снижение производительности. Хотя мы измеряем производительность двух схожих фрагментов: доступ к свойствам x и y экземпляров объекта Point путем вызова функции dotProduct в цикле.

Фрагмент 1 выполняется примерно в 3 раза быстрее, чем фрагмент 2. Единственное различие состоит в том, что мы используем отрицательные значения для свойств x и y объекта Point в фрагменте 2.

Анализ производительности фрагментов.

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

Прежде чем углубиться в наше исследование, давайте познакомимся с панелями инструмента системного анализатора. Инструмент имеет четыре основные панели:

  • панель временной шкалы для анализа событий Map/ICs во времени,
  • панель карт для визуализации деревьев переходов карт,
  • панель IC для получения статистики о событиях IC,
  • панель исходного кода для отображения позиций файлов Map/IC в скрипте.

Обзор системного анализатора

Группировка событий IC по имени функции для получения подробной информации о событиях IC, связанных с dotProduct.

Мы анализируем, как функция dotProduct может вызывать эту разницу в производительности. Поэтому мы группируем события IC по имени функции, чтобы получить более подробную информацию о событиях IC, связанных с функцией dotProduct.

Первое, что мы замечаем, это то, что у нас записаны два разных перехода состояний IC в этой функции. Один из неинициализированного до мономорфного и другой из мономорфного до полиморфного. Полиморфное состояние IC указывает на то, что теперь мы отслеживаем более одной карты, связанных с объектами Point, и это полиморфное состояние хуже, так как необходимо выполнять дополнительные проверки.

Мы хотим понять, почему создаются несколько Map-форм для одного типа объектов. Для этого мы переключаем кнопку информации о состоянии IC, чтобы получить больше информации об адресах Map, переходящих от неинициализированных до мономорфных.

Дерево переходов Map, связанное с мономорфным состоянием IC.

Дерево переходов Map, связанное с полиморфным состоянием IC.

Для мономорфного состояния IC мы можем визуализировать дерево переходов и увидеть, что динамически добавляются только два свойства x и y, но в случае полиморфного состояния IC появляется новая Map с тремя свойствами isNegative, x и y.

Панель Map отображает информацию о позиции в файле, чтобы выделить позиции на панели Source.

Мы кликаем на раздел с позицией файла на панели Map, чтобы увидеть, где в исходном коде добавляется свойство isNegative, и можем использовать эту информацию для устранения регресса производительности.

Теперь встаёт вопрос: как мы можем устранить регресс производительности, используя полученные из инструмента данные?

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

Теперь обновленный класс Point выглядит следующим образом:

class Point {
constructor(x, y) {
this.isNegative = x < 0 || y < 0;
this.x = x;
this.y = y;
}

dotProduct(other) {
return this.x * other.x + this.y * other.y;
}
}

Если мы снова выполним скрипт с модифицированным классом Point, мы увидим, что выполнение двух фрагментов, определенных в начале кейса, станет весьма схожим.

В обновленном трейсинге мы видим, что полиморфное состояние IC избегается, так как мы не создаем несколько Map для одного типа объектов.

Дерево переходов Map модифицированного объекта Point.

Анализатор системы

Теперь давайте подробно рассмотрим разные панели, которые присутствуют в анализаторе системы.

Панель шкалы времени

Панель шкалы времени позволяет выбирать моменты времени, что позволяет визуализировать состояния IC/Map в дискретные моменты времени или в выбранный временной интервал. Она поддерживает функции фильтрации, такие как увеличение/уменьшение масштаба событий журнала для выбранных временных диапазонов.

Обзор панели шкалы времени

Обзор панели шкалы времени (продолжение)

Панель Map

Панель Map имеет две подпанели:

  1. Детали Map
  2. Переходы Map

Панель Map визуализирует деревья переходов выбранных Map. Метаданные выбранной Map отображаются через подпанель деталей Map. С помощью интерфейса можно искать определенное дерево переходов, связанное с адресом Map. В подпанели статистики, которая расположена над подпанелью переходов Map, можно увидеть статистику о свойствах, вызывающих переходы Map, и о типах событий Map.

Обзор панели Map

Обзор панели статистики

Панель IC

Панель IC отображает статистику о событиях IC, попадающих в определенный временной диапазон, который фильтруется через панель шкалы времени. Кроме того, панель IC позволяет группировать события IC на основе различных критериев (тип, категория, Map, позиция в файле). Из параметров группировки параметры Map и позиция в файле взаимодействуют соответственно с панелями Map и исходного кода, чтобы отображать деревья переходов Map и выделять позиции файла, связанные с событиями IC.

Обзор панели IC

Обзор панели IC (продолжение)

Обзор панели IC (продолжение)

Обзор панели IC (продолжение)

Панель исходного кода

Панель исходного кода отображает загруженные скрипты с кликабельными маркерами для запуска пользовательских событий, которые выбирают как Map, так и IC события журнала в пользовательских панелях. Выбор загруженного скрипта можно выполнить через строку углубления. Выбор позиции файла из панели Map или панели IC выделяет выбранную позицию файла на панели исходного кода.

Обзор панели исходного кода

Благодарности

Я хотел бы поблагодарить всех из команд V8 и Web on Android, особенно моего руководителя Сатью и ко-руководителя Камилло за поддержку на протяжении всей моей стажировки и за возможность работать над таким классным проектом.

Я прекрасно провел лето, стажируясь в Google!