Подтвердить что ты не робот

Как именно оптимизатор 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, поскольку test2 и test3 приводят к точному количеству операций в секунду.

С другой стороны, деление, кажется, не оптимизировано (т.е. не вычисляется), так как test1 значительно быстрее.

  • Почему константа не вычисляется заранее, если вы не делаете это вручную в этом случае?
  • Можно ли увидеть, как V8 точно оптимизирует функции на определенной веб-странице?
4b9b3361

Ответ 1

Уверенное предположение о вашем первом вопросе:

Строго говоря, он не может постоянно сгибать часть pi / 180, потому что вы не выполняете pi / 180 во второй и третьей функции. Вы просто разделите (x * pi) на 180 (приоритет имеет умножение).

Теперь вы можете спросить, почему он не меняет порядок операций, чтобы завершить что-то, что он может оптимизировать (этот процесс называется пересоединением, кстати)... в конце концов, результат эквивалентен (a * b / c = (a * b) / c). Математика так говорит, верно?

Ну, математика так говорит, но математика не использует числа с плавающей запятой. С поплавками все сложнее. x * pi может быть округлено, а затем переупорядочение приведет к другому результату. Ошибки, вероятно, будут крошечными, но все же ведущим правилом оптимизации компилятора является: вы не должны изменять результат программы. Лучше выполнять субоптимальное на нескольких математических тестах, написанных неудачным способом, чем отключение пикселя (да, это может быть заметно) в некоторых графических кодах.

Ответ 2

Чтобы ответить на второй вопрос, вы можете увидеть байт-код, который V8 оптимизировал ваш JS для использования этого инструмента: http://mrale.ph/irhydra/2/. Это фантастика для низкоуровневой настройки кода в Chrome.