Убийцы оптимизации JS уже не такие страшные

в 11:32, , рубрики: javascript, V8, оптимизация

Год назад я увидела перевод Убийцы оптимизации, и была удивлена тем, сколько нужно держать в голове, чтобы писать оптимизированный js код. Практически весь es6 попадал под деоптимизацию.

Убийцы оптимизации JS уже не такие страшные - 1

Новый оптимизатор в v8, называемый TurboFan, за последний год научился оптимизировать практически весь es5/es6 и даже try-catch больше не является проблемой.

class TestClass {
    megaFunc() {
        try {
            let sum = 0;
            for (let val of [1, 2, 3]) {
                sum += val;
            }
            throw new Error(`sync error, sum = ${sum}`);
        }
        catch(err) {
            return err;
        }
    }
}
let test = new TestClass();
checkOptimizationStatus(test.megaFunc);

Function is optimized by TurboFan

Что осталось не оптимизированным, а так же как проверить свою функцию на предмет оптимизации или деоптимизации буквально в 1 действие можно увидеть под катом

2. Неподдерживаемый синтаксис

На данный момент не оптимизируются:

  • функции-генераторы;
  • функции, содержащие выражение for-of;
  • функции, содержащие выражение try-catch;
  • функции, содержащие выражение try-finally;
  • функции, содержащие составной оператор присваивания let;
  • функции, содержащие составной оператор присваивания const;
  • функции, содержащие объектные литералы, которые, в свою очередь, содержат объявления __proto__, get или set.

Скорее всего, неоптимизируемы:

  • функции, содержащие выражение debugger;
  • функции, вызывающие eval();
  • функции, содержащие выражение with.

Год назад этот список казался внушительным, на данный момент из списка не оптимизируется только debugger, генераторы и "__proto__, get или set", даже try-catch больше не требует трюка с tryCatch.

3. Использование arguments

Существует немало способов использовать arguments так, что оптимизировать функцию будет невозможно. Так что при работе с arguments следует быть особенно осторожными.
...

С arguments все просто, скорее всего их не будут оптимизировать, и уже достаточно легко можно перейти на rest параметры, с которыми можно работать как угодно.

4. Switch-case

Выражение switch-case на сегодняшний день может иметь до 128 пунктов case, и если превысить это количество, то содержащая данное выражение функция не сможет быть оптимизирована.

Теперь даже 500 case не вызывают деоптимизацию, 600 тоже.

5. For-in

Выражение For-in может несколькими способами помешать оптимизации функции. 5.1. Ключ не является локальной переменной
5.2. Итерируемый объект не является «простым перечисляемым»
5.2.2. В цепочке прототипов объекта есть поля с перечисляемыми значениями
5.2.3. Объект содержит перечисляемые индексы массива

Оптимизируется все, кроме ситуации когда key для for-in определен извне функции (но так всё равно никто писать не будет):

var key;
function nonLocalKey2() {
    var obj = {}
    for(key in obj);
}

6. Бесконечные циклы со сложной логикой условий выхода либо с неясными условиями выхода

Не удалось подобрать такой бесконечный цикл, чтобы получилась деоптимизация.

Как проверить свои функции на предмет оптимизации самостоятельно

Сделать это достаточно просто и для хрома и для ноды. В обоих случаях нужно всего лишь запустить их с флагом --allow-natives-syntax

Для chrome создаем ярлык:

"C:Program Files (x86)GoogleChromeApplicationchrome.exe" --js-flags="--allow-natives-syntax"

Файлы index.html

<script src="index.js"></script>

и index.js

function exampleFunction() {
    return 3;
    eval('');
}

checkOptimizationStatus(exampleFunction)

function checkOptimizationStatus(exampleFunction) {
    exampleFunction();
    exampleFunction();
    %OptimizeFunctionOnNextCall(exampleFunction);
    exampleFunction();

    switch (%GetOptimizationStatus(exampleFunction)) {
        case 1: console.log("Function is optimized"); break;
        case 2: console.log("Function is not optimized"); break;
        case 3: console.log("Function is always optimized"); break;
        case 4: console.log("Function is never optimized"); break;
        case 6: console.log("Function is maybe deoptimized"); break;
        case 7: console.log("Function is optimized by TurboFan"); break;
        default: console.log("Unknown optimization status"); break;
    }
}

И просто открываем index.html в браузере. Необходимости в веб-сервере нет, просто обычная html страничка.

Для node еще проще:

node --allow-natives-syntax index.js

Функция-обертка checkOptimizationStatus(yourFunction) покажет статус оптимизации, достаточно вызвать ее передав в качестве параметра вашу функцию

Итог

Новые оптимизация появляются достаточно быстро, поэтому просто выкиньте из головы ужасный список убийц оптимизаций и спокойно пишите на js в привычном стиле

PS: В chrome 55 появилась поддержка async-await без флага, в ноде начиная с 8 ветки, промис-функции успешно оптимизируются, значит не долго ждать когда и async-await тоже будут оптимизироваться.

Автор: Maiami

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js