#javascript #optimization #google-chrome #inline #v8
#javascript #оптимизация #google-chrome #встроенный #v8
Вопрос:
Мне интересно, возможно ли получить информацию о том, как именно V8 оптимизирует и упорядочивает вещи.
Я создал три простые тестовые функции, которые вычисляют синус угла в градусах. Я поместил их все в замыкания, чтобы V8 мог встроить локальные переменные.
1. Используя предварительно вычисленную константу Math.PI / 180
, а затем выполните Math.sin(x * constant)
.
Я использовал этот код:
var test1 = (function() {
var constant = Math.PI / 180; // only calculate the constant once
return function(x) {
return Math.sin(x * constant);
};
})();
2. Вычисление константы на лету.
var test2 = (function() {
var pi = Math.PI; // so that the compiler knows pi cannot change
// and it can inline it (Math.PI could change
// at any time, but pi cannot)
return function(x) {
return Math.sin(x * pi / 180);
};
})();
3. Использование буквенных чисел и вычисление константы на лету.
var test3 = (function() {
return function(x) {
return Math.sin(x * 3.141592653589793 / 180);
};
})();
Удивительно, но результаты были следующими:
test1 - 25,090,305 ops/sec
test2 - 16,919,787 ops/sec
test3 - 16,919,787 ops/sec
Похоже, что pi
он был встроен в test2
as test2
и test3
приводит к точно такому же количеству операций в секунду.
С другой стороны, разделение, похоже, не оптимизировано (т. Е. Предварительно вычислено), поскольку test1
выполняется значительно быстрее.
- Почему константа не вычисляется предварительно, если вы не делаете этого вручную в этом случае?
- Можно ли увидеть, как V8 точно оптимизирует функции на определенной веб-странице?
Комментарии:
1. Я не уверен, распространяется ли концепция встраивания стиля C на виртуальные машины с JITed. Я просто размышляю, но я подозреваю, что V8 применяет оптимизацию времени выполнения к функциям, которые часто вызываются, но это, вероятно, трудно предсказать.
2. @mikerobi: Это может быть наивный вопрос — но разве нельзя было бы просто посмотреть, что делает V8 во время компиляции / оптимизации / встраивания, как какой-нибудь инструмент отладки?
3. Вероятно, это возможно, но я сомневаюсь, что кто-либо за пределами команды разработчиков V8 мог бы рассказать вам, как.
4. Ну, например, JIT в PyPy имеет обширное ведение журнала, и они начали создавать инструмент для просмотра всех промежуточных представлений, через которые проходил код (байт-код Python, JIT IR, машинный код), поэтому теоретически такие вещи должны быть возможны. Но я предполагаю, что это упрощается тем фактом, что (1) фактический JIT генерируется машиной и (2) это JIT трассировки.
5. На данный момент V8 не выполняет постоянное распространение. Также вы наказываете V8, создавая функции в цикле (настройка вызывается несколько раз): попробуйте модифицированный тестовый пример jsperf.com/optimizing-v8/2 вместо этого. Это устраняет накладные расходы, связанные с многократной повторной оптимизацией новорожденных экземпляров замыканий, и делает картину более четкой.
Ответ №1:
Обоснованное предположение по вашему первому вопросу:
Строго говоря, он не может постоянно складывать pi / 180
часть, потому что вы этого не делаете pi / 180
во второй и третьей функциях. Вы просто делите (x * pi)
на 180
(умножение имеет приоритет).
Теперь вы можете спросить, почему он не изменяет порядок операций, чтобы получить что-то, что он может оптимизировать (кстати, этот процесс называется повторной ассоциацией) … в конце концов, результат эквивалентен ( a * b / c = (a * b) / c
). Так говорит математика, верно?
Ну, математика так говорит, но математика не использует числа с плавающей запятой. С плавающими значениями все сложнее. x * pi
может быть округлено, и тогда изменение порядка приведет к другому результату. Ошибки, вероятно, будут незначительными, но, тем не менее, главное правило оптимизации компилятора таково: вы не должны изменять результат программы. Лучше выполнить неоптимальные несколько математических тестов, написанных неудачным образом, чем отклониться на пиксель (да, это может быть заметно) в каком-либо графическом коде.
Комментарии:
1. Примечание на случай, если кто-то сомневается в моем утверждении, что функции будут давать разные результаты: Попробуйте это с
x = 0.6784993546113602
, например. Я нашел это и многие другие числа сMath.random()
.2. Спасибо, это очень интересно. Это требует дополнительных исследований (например, с константой
(a * b) * x
).3. Использование
pi * 180 * x
действительно приводит к получению разных результатов — nowtest1
иtest3
равны, ноtest2
стали медленнее. Я думаю, это можно объяснить с помощью operator precendece. Я собираюсь попытаться найти причину, по которойpi
intest2
больше не будет встроен. jsperf.com/optimizing-v8-24. в таком случае, что это
x * (pi / 180)
дает? Компилятор теоретически мог бы вытащитьpi / 180
прямо сейчас.5. @cwolves: Когда я сравниваю свой первоначальный тест и тот же тест с добавлением круглых скобок, кажется, что улучшений практически нет: jsperf.com/optimizing-v8/2 и jsperf.com/optimizing-v8/3 . Это означало бы, что идея приоритета с плавающей запятой / оператора на самом деле не применима. Загадочно…
Ответ №2:
Чтобы ответить на ваш второй вопрос, вы можете увидеть байт-код, для которого V8 оптимизировал ваш JS с помощью этого инструмента:http://mrale.ph/irhydra/2 / . Это фантастика для низкоуровневой настройки кода в Chrome.