eval() тормозит другой код, почему?
Использование eval в любом месте программы замедляет работу другого кода, даже если он никогда не выполняется. Почему это?
В приведенном ниже примере есть код:
var data = Array.apply(null, Array(10000)).map(function(_, i) {
return i;
});
function notCalled() {
eval();
}
function simpleFor (d){
for (var i = 0, len = d.length; i < len; i += 1) {
d[i] = d[i] + 1;
}
}
Если eval()
закомментировано как simpleFor()
и использование для цикла имеют сопоставимую производительность. когда eval()
есть ли нативный цикл, замедление составляет ~85%. Я тестировал Chrome/Firefox из jsperf и, используя похожий скрипт, nodejs.
Пример находится по адресу: http://jsperf.com/eval-wierdness
Моя оригинальная идея, и как я нашел это, заключалась в создании функции для создания эффективных функций отображения, таких как:
// naive first implementation
var emapcache = {};
function emap(fs, list) {
var f = emapcache[fs];
if (!f) {
f = emapcache[fs] = eval('(function (data) { for (var i = 0, s = data.length; i < s; i++) { data[i] = ' + fs.replace(/x/g, '(data[i])') + ';}})');
}
return f(list);
}
Можно ли сделать такую функцию эффективной, не замедляя работу другого кода?
2 ответа
Вы можете сделать это new Function('param', 'body')
var emapcache = {};
function emap(fs, list) {
var f = emapcache[fs];
if (!f) {
f = emapcache[fs] = new Function('data', 'for (var i = 0, s = data.length; i < s; i++) { data[i] = ' + fs.replace(/x/g, '(data[i])') + ';}');
}
return f(list);
}
Вообще говоря, eval
ломает многие оптимизации компилятора. В частности, в этом фрагменте кода он замедляет другой код, потому что он может принимать как глобальные, так и локальные области действия в оцениваемый код. Это нарушает некоторую оптимизацию, которую может выполнить JIT-компилятор, потому что переменные в цикле должны отслеживаться ради eval
, Но в simpleFor
Движку не нужно заботиться о переменных (так что функция может быть легко кэширована или около того).
Сложно сказать Function
построить лучше, чем eval
в смысле безопасности, но Function
не учитывает локальную область видимости, поэтому может быть быстрее.
У меня есть эталонный тест, чтобы проиллюстрировать разницу в производительности между этими двумя. http://jsperf.com/eval-vs-func-so-question
Большинство современных движков JavaScript не оценивают код напрямую. Вместо этого они компилируют код в некоторую промежуточную форму, оптимизируют его и затем исполняют.
Тем не менее, когда вы используете eval()
весь код программы недоступен, когда скрипт предварительно скомпилирован. Это означает, что многие оптимизации просто невозможны.