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()весь код программы недоступен, когда скрипт предварительно скомпилирован. Это означает, что многие оптимизации просто невозможны.

Другие вопросы по тегам