Комментарии 62
Я правильно понимаю, что, теоретически, это может дать прирост и в интерпретаторах других браузеров?
И практически. Весь этот текст можно сжать до фразы «Пишем как можно более типизированно» (типизированные массивы, не перегруженные функции, статический набор полей у объектов).
Тот же TypeScript кроме сахаров помогает писать быстрый код за счет статической проверки типов(именно помогает, а не делает).
Тот же TypeScript кроме сахаров помогает писать быстрый код за счет статической проверки типов(именно помогает, а не делает).
Почему JavaScript изначально такой? Как хорошо было бы, если бы он был типизированным.
Так исторически сложилось. (с)
НЛО прилетело и опубликовало эту надпись здесь
Есть Dart…
Он есть, но его и нет. Точнее он пока в стадии «Посмотреть и поиграться», а JavaScript есть уже сейчас и с ним надо что-то делать.
Ну как, всё, кроме HTTP-клиента/сервера и сокетов для нативной части реализовали. Библиотека обширная, интероп с JS умеет. Так что можно использовать.
К сожалению, это только хром. Трансляции в JS он, конечно, поддается, но с костылями. Например, попробуйте транслировать генераторы в JS кроме них есть еще много других примеров. Dart язык очень хороший, но распостраненность — это его огромная проблема (которая в ближейшие лет 5 точно не решится).
как это только хром? постоянно гоняем тесты dart2js на FF, Safari, IE: build.chromium.org/p/client.dart/console
dart2js конечно хорошо, но в Dart есть такие штуки не свойственные для JavaScript и dart2js делает их костылями. Нет генераторов, нет Image, нет декларативных классов, нет честных изолятов (изоляты воркерах не могут трогать DOM). Ты же сам прекрасно знаешь их фундаментальные различия :)
Dart это очень хорошо, но я бы пока не стал его использовать в продакшене.
Dart это очень хорошо, но я бы пока не стал его использовать в продакшене.
> К сожалению, это только хром. Трансляции в JS он, конечно, поддается, но с костылями.
Байка. Рекомендую почитать вот этот пост Сета Лэдда: blog.sethladd.com/2012/10/9-dart-myths-debunked.html
Байка. Рекомендую почитать вот этот пост Сета Лэдда: blog.sethladd.com/2012/10/9-dart-myths-debunked.html
Вот AS3 тоже ECMAScript, но с типами — совсем другая песня :)
НЛО прилетело и опубликовало эту надпись здесь
Сначала мы дадим вам возможность сохранять в переменную значение любого типа.
Потом начнем бить по рукам, чтобы вы этого не делали.
Потом начнем бить по рукам, чтобы вы этого не делали.
НЛО прилетело и опубликовало эту надпись здесь
Это не упрек, это на тему, что, если плохо придумано сначала, то надо сломать и переделать.
Не бить по рукам, а всего лишь толсто намекать, что использование этой опции имеет очень ненулевую цену…
Местами не правильно перевели, например:
1. Массивы бывают двух видов — хранящие 31 битные инты и указатели на другие обьекты(этой разницей бит и кушается) и только числа двойной точности.
Чем плохо — был массив с двумя интами, под него выделилилось место для хранения 4х. Вы добавили double и массив перепаковался в типизированный. Старые данные уходят в GC. Добавили строку — обратная перепаковка. Ваш double теперь доступен через указатель, что сильно медленно
2. Полиморфные функции не медленее чем мономорфные. Это буду две разные функции, каждая из которых потребует своей оптимизации( 0.5-1мсек на функцию, это не мало). IC(inline cache) может чуть раньше выбрать какую он будет использовать.
3. Огромная Сила в inline функциях! Жалко что хром не инлайнит функции которые требуют переключение контекста. Да, именно в JS функциональное программирование реально быстрее чем старый добрый ООП.
Вообще профилирование через d8 или запуск хрома с флагами чтука относительно бесполезная. Позволяет оттюнить только синтетические примеры и реально узкие места.
1. Массивы бывают двух видов — хранящие 31 битные инты и указатели на другие обьекты(этой разницей бит и кушается) и только числа двойной точности.
Чем плохо — был массив с двумя интами, под него выделилилось место для хранения 4х. Вы добавили double и массив перепаковался в типизированный. Старые данные уходят в GC. Добавили строку — обратная перепаковка. Ваш double теперь доступен через указатель, что сильно медленно
2. Полиморфные функции не медленее чем мономорфные. Это буду две разные функции, каждая из которых потребует своей оптимизации( 0.5-1мсек на функцию, это не мало). IC(inline cache) может чуть раньше выбрать какую он будет использовать.
3. Огромная Сила в inline функциях! Жалко что хром не инлайнит функции которые требуют переключение контекста. Да, именно в JS функциональное программирование реально быстрее чем старый добрый ООП.
Вообще профилирование через d8 или запуск хрома с флагами чтука относительно бесполезная. Позволяет оттюнить только синтетические примеры и реально узкие места.
1. По типу содержимого быстрые элементы бывают трех видов и трансформируются в одном направлении от менее общих к более общим: smi (small integer) -> double -> object. Бывают еще дырявые (holey) и непрерывные (packed). Еще бывают медленные элементы — представляются словарем.
2. Рекомендация соблюдать мономорфизм относится не к функциям, а к отдельным операциям, например, оператору умножения, оператору [] или вызову метода. Операции работают быстрее, когда они мономорфны — выполнятся над объектами одного и того же типа/скрытого класса. Как вы определяете «полиморфную функцию»?
3. V8 инлайнит функции, которые требуют переключения контекста на ia32 (Mac, Windows), но не инлайнит на x64/arm. Что касается функционального программирования, то зависит от того какие именно паттерны мы сравниваем. В большинстве случаев ООП основанное на связке constructor + prototype chain имеет больше шансов быть хорошо заоптимизированным. Можно почитать мой блог о том, какие именно проблемы возникают если опираться на замыкания: mrale.ph/blog/2012/09/23/grokking-v8-closures-for-fun.html
А собственно кроме узких мест ничего никогда тюнить и не надо. Все советы по оптимизации бесполезны в 99% случаев, пока вы не наткнетесь на случай попадающий в злые 1%, где надо костьми лечь но выжать все что можно.
2. Рекомендация соблюдать мономорфизм относится не к функциям, а к отдельным операциям, например, оператору умножения, оператору [] или вызову метода. Операции работают быстрее, когда они мономорфны — выполнятся над объектами одного и того же типа/скрытого класса. Как вы определяете «полиморфную функцию»?
3. V8 инлайнит функции, которые требуют переключения контекста на ia32 (Mac, Windows), но не инлайнит на x64/arm. Что касается функционального программирования, то зависит от того какие именно паттерны мы сравниваем. В большинстве случаев ООП основанное на связке constructor + prototype chain имеет больше шансов быть хорошо заоптимизированным. Можно почитать мой блог о том, какие именно проблемы возникают если опираться на замыкания: mrale.ph/blog/2012/09/23/grokking-v8-closures-for-fun.html
А собственно кроме узких мест ничего никогда тюнить и не надо. Все советы по оптимизации бесполезны в 99% случаев, пока вы не наткнетесь на случай попадающий в злые 1%, где надо костьми лечь но выжать все что можно.
Вы давно видели 32х битные Маки?
Долго копал и свой код, и исходники v8 чтобы понять как можно заставить заинланиться некоторые мои функции (например математические).
Но у вас в блоге заметил странную магию с «use strict», который не дает захватывать контекст.
Можете эту магию прокоментировать, и вообще дать пару разъяснений про strict mode. Это лексический режим, или оно влияет и на генерируемый ассемблер?
Долго копал и свой код, и исходники v8 чтобы понять как можно заставить заинланиться некоторые мои функции (например математические).
Но у вас в блоге заметил странную магию с «use strict», который не дает захватывать контекст.
function sk(x, y) { // x and y are not context allocated
"use strict";
return arguments[0] * arguments[1];
}
Можете эту магию прокоментировать, и вообще дать пару разъяснений про strict mode. Это лексический режим, или оно влияет и на генерируемый ассемблер?
> Вы давно видели 32х битные Маки?
Chrome на Mac по сей день 32-битный
64-битная сборка только на Linux используется AFAIK
> Но у вас в блоге заметил странную магию с «use strict», который не дает захватывать контекст.
В данном конкретном примере
Strict mode описан в стандарте: es5.github.com/#C
> как можно заставить заинланиться некоторые мои функции (например математические).
Если вы набросаете примеры кода, то можно будет обсудить почему что-то не инлайнится и какая будет польза от инлайна :-)
Chrome на Mac по сей день 32-битный
omega ~ ∳ file /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome: Mach-O executable i386
64-битная сборка только на Linux используется AFAIK
> Но у вас в блоге заметил странную магию с «use strict», который не дает захватывать контекст.
В данном конкретном примере
"use strict"
разрывает связь между arguments
и формальными параметрами. Это убирает создание контекста и позволяет инлайнить этот код (если функция создает контекст, то такую пока V8 не инлайнит).Strict mode описан в стандарте: es5.github.com/#C
> как можно заставить заинланиться некоторые мои функции (например математические).
Если вы набросаете примеры кода, то можно будет обсудить почему что-то не инлайнится и какая будет польза от инлайна :-)
пример банален
Кстати, пользуясь, случаяем хотел бы уточнить насчет хранения двордов. Что же лучше — массив(который будет создан уже перепакованным?) или хэш с .x\.y(который тоже вообще бинарная структура вроде)?
var vector = {
add: function (a,b){
return [a[0]+b[0],a[1]+b[1]]
}
}
//и где-то в совершенно другом месте vector.add не инлайнится
При этом, если бы я использовал просто функцию vector_add в глобальном scope - инлайн был бы возможен.
Кстати, пользуясь, случаяем хотел бы уточнить насчет хранения двордов. Что же лучше — массив(который будет создан уже перепакованным?) или хэш с .x\.y(который тоже вообще бинарная структура вроде)?
эта функция не заинлайнится из-за array literal даже если будет в глобальном scope: code.google.com/p/v8/issues/detail?id=1322
если vector существует в единственном числе, то тут разницы между глобальным и не глобальным scope не должно быть.
Хранение 32-битных величин сложный вопрос. На ia32 те из них которые не влезают в smi (31-bit signed integer) превратятся в полновесные числа с плавающей точкой. Зависит от многих факторов: как много тех кто не влезает в 31бит, как они будут использоваться и т.д. Int32Array может оказаться оптимальным в некоторых случаях.
если vector существует в единственном числе, то тут разницы между глобальным и не глобальным scope не должно быть.
Хранение 32-битных величин сложный вопрос. На ia32 те из них которые не влезают в smi (31-bit signed integer) превратятся в полновесные числа с плавающей точкой. Зависит от многих факторов: как много тех кто не влезает в 31бит, как они будут использоваться и т.д. Int32Array может оказаться оптимальным в некоторых случаях.
Можно поподробнее про силу инлайна?
var a = [1, 2, 3, 4];
// Если выполнять это честно, то функция будет вызвана на каждом элементе - это дорого
// Но такая запись более читаема
a.forEach(function (item) {
console.log(item);
});
// Такие экспрешены инлайнится (где это возможно) в стейтмент for () {}
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
только вот такой оптимизации V8 не делает пока :-)
В общем самое вкусное покуда не инлайнится.
А тогда каким образом это работает быстро? Не над каждым же элементом честно функция вызывается.
следует определить понятие быстро :-)
над каждым элементом функция честно вызывается. в зависимости от того, что функция делает цена этого вызова может быть заметна, а может быть амортизированна и не очень заметна.
над каждым элементом функция честно вызывается. в зависимости от того, что функция делает цена этого вызова может быть заметна, а может быть амортизированна и не очень заметна.
быстро всмысле сопоставимо со стейтментом for
ну по сравнению с циклом
for
если зарядить на длинном массиве, много-много раз пустую функцию то будет видна цена вызова.Кстати, а что мешает заинлайнить такой цикл? (понятно, что мой пример был предельно тривиальный)
Другой вопрос, а что мешает заинлайнить пустые функции? Точнее привести их к виду — не вызывать, все undefined возвращать. Ведь функции — immutable и тело у них самом собой появиться не может.
А что нужно делать вместо удаления элементов из массивов?
Либо использовать хэши, либо закрывать дырку в массиве, либо ответить на вопрос зачем удаляем.
А вы сами используете массивы, только когда не предполагается удалять из них элементы?
Я использую массивы для разных целей.
Но никогда не оставляю в них дырки.
Тут лучше обойтись хэшом.
Но никогда не оставляю в них дырки.
Тут лучше обойтись хэшом.
А удаление с конца массива тоже оставляет дырки в его внутреннем представлении? Где можно подробнее почитать на этот счет?
когда говорится, что лучше не удалять элементы, имеется ввиду удаление многих элементво из середины оператором delete.
вы уверены про delete? он не удаляет элемент, а прописывает undefined
splice удаляет элементы из середины
splice удаляет элементы из середины
мы, видимо, по разному определяем понятие удалить.
прежде всего
дырки это та проблема, которой рекомендации рекомендуют избегать, чтение из дырки требует поиска свойства через цепочку прототипов и оптимизированный код не любит этого и просто деоптимизируется. К тому же если у вас в массиве много дырок, то V8 может внезапно решить, что память важнее производительности и ради экономии места превратит длинный дырявый массив в словарь.
прежде всего
delete
не прописывает undefined
, он удаляет свойство полностью оставляя вместо него дырку. разницу просто увидеть на примере кода:a = [1];
b = [1];
Array.prototype[0] = 42;
console.log(a[0] + " " + b[0]); // 1 1
delete a[0];
b[0] = undefined;
console.log(a[0] + " " + b[0]); // 42 undefined
дырки это та проблема, которой рекомендации рекомендуют избегать, чтение из дырки требует поиска свойства через цепочку прототипов и оптимизированный код не любит этого и просто деоптимизируется. К тому же если у вас в массиве много дырок, то V8 может внезапно решить, что память важнее производительности и ради экономии места превратит длинный дырявый массив в словарь.
splice
же, который удаляет и сдвигает элементы не оставляет после себя дырок, поэтому безобиден в данном отношении. Следует, впрочем, всегда помнить, что он имеет линейную сложность по количеству сдвигаемых элементов.Можно юзать как стек или очередь.
А что нужно делать вместо удаления элементов из массивов?Удалять-то можно; а нужно не оставлять дырки.
Поэтому вместо «delete arrayName[index]» следует использовать
я бы сказал, что это спорная рекомендация и они совсем не эквивалентны. все конечно же зависит от конкретного кода,
splice
, например, линейная операция по количеству сдвигаемых элементов. в каких-то случаях может лучше arr[i] = null;
делать (хоть оно и не эквивалентно delete
)Я как раз и использую
splice
. Однако он медленнее, чем просто устанавливать значение равным null, либо делать delete
. Но ведь это тоже не всегда приемлимо. Было бы хорошо услышать реальные примеры задач и те цифры, которые мы выигрываем/проигрываем, используя один или другой метод.Спасибо за перевод! Собрал d8, но похоже это было самой простой задачей)
--trace-opt, --trace-deopt — ничего не выводят, и так и эдак пробовал. Есть какой-нибудь код, который гарантировано выведет инфу?
--trace-bailout — похоже выпилили. В Changelog (Version 3.13.4): Print reason for disabling optimization. Kill --trace-bailout flag. По крайней мере сейчас, ругается на несуществующий флаг.
--trace-opt, --trace-deopt — ничего не выводят, и так и эдак пробовал. Есть какой-нибудь код, который гарантировано выведет инфу?
--trace-bailout — похоже выпилили. В Changelog (Version 3.13.4): Print reason for disabling optimization. Kill --trace-bailout flag. По крайней мере сейчас, ругается на несуществующий флаг.
НЛО прилетело и опубликовало эту надпись здесь
> В частности, оптимизирующий компилятор пропускает любые функции, содержащие блоки try/catch
Интересно, значит ли это, что jQuery толком не оптимизируется? или для него сделано исключение?
Просто у меня стойкая ассоциация в мозгу, что
Интересно, значит ли это, что jQuery толком не оптимизируется? или для него сделано исключение?
Просто у меня стойкая ассоциация в мозгу, что
jQuery('...') === try { jQuery('..') } catech(e) {};
> Инициализируйте все объекты в конструкторах, чтобы они как можно меньше менялись в дальнейшем.
Спасибо, погуглил, ускорил стандартный setTimeout в node.js на 15-20%.
github.com/joyent/node/issues/4182
Спасибо, погуглил, ускорил стандартный setTimeout в node.js на 15-20%.
github.com/joyent/node/issues/4182
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Оптимизируем производительность JavaScript для V8