Indicium: инструмент трассировки времени выполнения V8
Последние три месяца были для меня потрясающим опытом обучения, так как я присоединилась к команде V8 (Google London) в качестве стажера и работала над новым инструментом под названием Indicium.
Этот системный анализатор представляет собой унифицированный веб-интерфейс для трассировки, отладки и анализа шаблонов создания и модификации Inline Caches (ICs) и карт (Maps) в реальных приложениях.
В V8 уже существует инфраструктура трассировки для ICs и карт (Maps), которая может обрабатывать и анализировать события IC с помощью IC Explorer и события карт с помощью Map Processor. Однако предыдущие инструменты не позволяли анализировать карты и ICs комплексно, что теперь возможно с системным анализатором.
Исследование
Давайте рассмотрим пример, чтобы продемонстрировать, как мы можем использовать 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 в скрипте.
Мы анализируем, как функция dotProduct
может вызывать эту разницу в производительности. Поэтому мы группируем события IC по имени функции, чтобы получить более подробную информацию о событиях IC, связанных с функцией dotProduct
.
Первое, что мы замечаем, это то, что у нас записаны два разных перехода состояний IC в этой функции. Один из неинициализированного до мономорфного и другой из мономорфного до полиморфного. Полиморфное состояние IC указывает на то, что теперь мы отслеживаем более одной карты, связанных с объектами Point
, и это полиморфное состояние хуже, так как необходимо выполнять дополнительные проверки.
Мы хотим понять, почему создаются несколько Map-форм для одного типа объектов. Для этого мы переключаем кнопку информации о состоянии IC, чтобы получить больше информации об адресах Map, переходящих от неинициализированных до мономорфных.
Для мономорфного состояния IC мы можем визуализировать дерево переходов и увидеть, что динамически добавляются только два свойства x
и y
, но в случае полиморфного состояния IC появляется новая Map с тремя свойствами isNegative
, x
и y
.
Мы кликаем на раздел с позицией файла на панели 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 для одного типа объектов.
Анализатор системы
Теперь давайте подробно рассмотрим разные панели, которые присутствуют в анализаторе системы.
Панель шкалы времени
Панель шкалы времени позволяет выбирать моменты времени, что позволяет визуализировать состояния IC/Map в дискретные моменты времени или в выбранный временной интервал. Она поддерживает функции фильтрации, такие как увеличение/уменьшение масштаба событий журнала для выбранных временных диапазонов.
Панель Map
Панель Map имеет две подпанели:
- Детали Map
- Переходы Map
Панель Map визуализирует деревья переходов выбранных Map. Метаданные выбранной Map отображаются через подпанель деталей Map. С помощью интерфейса можно искать определенное дерево переходов, связанное с адресом Map. В подпанели статистики, которая расположена над подпанелью переходов Map, можно увидеть статистику о свойствах, вызывающих переходы Map, и о типах событий Map.
Панель IC
Панель IC отображает статистику о событиях IC, попадающих в определенный временной диапазон, который фильтруется через панель шкалы времени. Кроме того, панель IC позволяет группировать события IC на основе различных критериев (тип, категория, Map, позиция в файле). Из параметров группировки параметры Map и позиция в файле взаимодействуют соответственно с панелями Map и исходного кода, чтобы отображать деревья переходов Map и выделять позиции файла, связанные с событиями IC.
Панель исходного кода
Панель исходного кода отображает загруженные скрипты с кликабельными маркерами для запуска пользовательских событий, которые выбирают как Map, так и IC события журнала в пользовательских панелях. Выбор загруженного скрипта можно выполнить через строку углубления. Выбор позиции файла из панели Map или панели IC выделяет выбранную позицию файла на панели исходного кода.
Благодарности
Я хотел бы поблагодарить всех из команд V8 и Web on Android, особенно моего руководителя Сатью и ко-руководителя Камилло за поддержку на протяжении всей моей стажировки и за возможность работать над таким классным проектом.
Я прекрасно провел лето, стажируясь в Google!