Год назад я увидела перевод Убийцы оптимизации, и была удивлена тем, сколько нужно держать в голове, чтобы писать оптимизированный js код. Особенно расстраивало, что практически весь es6 попадал под деоптимизацию.
И вот новый оптимизатор в v8, называемый TurboFan, за последний год научился оптимизировать этот самый практически весь es6, es5 и даже try-catch больше не является проблемой.
Что осталось не оптимизированным, а так же как проверить свою функцию на предмет оптимизации или деоптимизации буквально в 1 действие можно увидеть под катом
Год назад этот список казался внушительным, на данный момент из списка не оптимизируется только debugger, генераторы и "__proto__, get или set", даже try-catch больше не требует трюка с tryCatch.
С arguments все просто, скорее всего их не будут оптимизировать, и уже достаточно легко можно перейти на rest параметры, с которыми можно работать как угодно.
Теперь даже 500 case не вызывают деоптимизацию, 600 тоже.
Оптимизируется все, кроме ситуации когда key для for-in определен извне функции (но так всё равно никто писать не будет):
Не удалось подобрать такой бесконечный цикл, чтобы получилась деоптимизация.
Сделать это достаточно просто и для хрома и для ноды. В обоих случаях нужно всего лишь запустить их с флагом --allow-natives-syntax
Для chrome создаем ярлык:
Файлы index.html
и index.js
И просто открываем index.html в браузере. Необходимости в веб-сервере нет, просто обычная html страничка.
Для node еще проще:
Функция-обертка checkOptimizationStatus(yourFunction) покажет статус оптимизации, достаточно вызвать ее передав в качестве параметра вашу функцию
Из внушительного списка убийц осталось буквально 2-3 некритичных случая, которые далеко не каждый использует.
Новые оптимизация появляются достаточно быстро, поэтому можно просто выкинуть из головы этот список и спокойно писать на js в привычном/удобном стиле.
PS: В chrome 55 появилась поддержка async-await без флага, в ноде начиная с 8 ветки, промис-функции успешно оптимизируются, значит не долго ждать когда и async-await тоже будут оптимизироваться.
UPD: В chrome canary 57 добавлена оптимизация для async-await
И вот новый оптимизатор в v8, называемый TurboFan, за последний год научился оптимизировать этот самый практически весь es6, es5 и даже 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)\Google\Chrome\Application\chrome.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 " + exampleFunction.name); break;
case 49: console.log("Function is optimized by NewMethod " + exampleFunction.name); break;
default: console.log("Unknown optimization status"); break;
}
}
И просто открываем index.html в браузере. Необходимости в веб-сервере нет, просто обычная html страничка.
Для node еще проще:
node --allow-natives-syntax index.js
Функция-обертка checkOptimizationStatus(yourFunction) покажет статус оптимизации, достаточно вызвать ее передав в качестве параметра вашу функцию
Итог
Из внушительного списка убийц осталось буквально 2-3 некритичных случая, которые далеко не каждый использует.
Новые оптимизация появляются достаточно быстро, поэтому можно просто выкинуть из головы этот список и спокойно писать на js в привычном/удобном стиле.
PS: В chrome 55 появилась поддержка async-await без флага, в ноде начиная с 8 ветки, промис-функции успешно оптимизируются, значит не долго ждать когда и async-await тоже будут оптимизироваться.
UPD: В chrome canary 57 добавлена оптимизация для async-await
Результаты для async-await функций
async function delayAsync(delay) {
return new Promise(resolve => {
setTimeout(() => resolve(), delay)
})
}
async function asyncTest() {
return 'habrahabr'
}
async function exampleFunction() {
try {
let result = await asyncTest()
await delayAsync(500)
console.log(`result after 500ms: ${result}`)
}
catch (err) {
console.error(err)
}
}
Function is optimized by TurboFan: exampleFunction
Function is optimized by TurboFan: asyncTest
Function is optimized by TurboFan: delayAsync
//ждем 500ms, после этого результат
(3) after 500ms: habrahabr