it-swarm-ru.tech

Влияет ли использование анонимных функций на производительность?

Мне было интересно, есть ли разница в производительности между использованием именованных функций и анонимных функций в Javascript? 

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

против

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

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

80
nickf

Здесь проблема производительности заключается в стоимости создания нового функционального объекта на каждой итерации цикла, а не в том факте, что вы используете анонимную функцию:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

Вы создаете тысячу различных функциональных объектов, даже если они имеют одинаковое тело кода и не привязаны к лексической области ( closure ). С другой стороны, следующее кажется более быстрым, поскольку оно просто назначает ссылку на функцию same для элементов массива в цикле:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

Если вы должны были создать анонимную функцию перед входом в цикл, а затем присваивать ссылки на нее только элементам массива, находящимся внутри цикла, вы обнаружите, что нет никакой производительности или семантической разницы по сравнению с версией именованной функции:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

Короче говоря, нет никакой заметной потери производительности при использовании анонимных функций над именованными.

Кроме того, сверху может показаться, что нет разницы между:

function myEventHandler() { /* ... */ }

а также:

var myEventHandler = function() { /* ... */ }

Первый является объявлением функции , а второй - присваиванием переменной анонимной функции. Хотя они могут показаться одинаковыми, JavaScript обрабатывает их немного по-разному. Чтобы понять разницу, я рекомендую прочитать « Неопределенность объявления функций JavaScript ».

Фактическое время выполнения любого подхода во многом будет зависеть от реализации браузером компилятора и времени выполнения. Для полного сравнения производительности современных браузеров посетите сайт JS Perf

81
Atif Aziz

Вот мой тестовый код:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

Результаты, достижения:
Тест 1: 142 мс Тест 2: 1983 мс

Похоже, что движок JS не распознает, что это одна и та же функция в Test2, и компилирует ее каждый раз.

21
nickf

Как общий принцип проектирования, вам следует избегать многократного использования одного и того же кода. Вместо этого вы должны поднять общий код в функцию и выполнить эту (общую, хорошо протестированную, легко изменяемую) функцию из нескольких мест.

Если (в отличие от того, что вы выводите из своего вопроса) вы объявляете внутреннюю функцию один раз и используете этот код один раз (и у вас нет ничего идентичного в вашей программе), тогда обрабатывается автономная функция возможно, (это, наверное, люди) таким же образом компилятором как обычная именованная функция.

Это очень полезная функция в определенных случаях, но не должна использоваться во многих ситуациях.

2
Tom Leys

Там, где мы можем оказать влияние на производительность, в операции объявления функций. Вот эталон объявления функций внутри контекста другой функции или за ее пределами:

http://jsperf.com/function-context-benchmark

В Chrome операция будет быстрее, если мы объявим функцию снаружи, но в Firefox все наоборот.

В другом примере мы видим, что если внутренняя функция не является чистой функцией, она будет иметь недостаточную производительность также в Firefox: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

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

Если вам будет проще найти код, производительность не является проблемой, если вы не собираетесь вызывать функцию миллионы раз.

1
Joe Skora

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

Но если вы не пишете процедуры шифрования/дешифрования или что-то подобное, чувствительное к производительности, как отмечали многие другие, всегда лучше оптимизировать для элегантного, легко читаемого кода поверх быстрого кода.

Предполагая, что вы пишете хорошо спроектированный код, вопросы скорости должны быть обязанностью тех, кто пишет интерпретаторы/компиляторы.

1
pcorcoran

@nickf

Это довольно глупый тест, хотя вы сравниваете выполнение и компиляцию время там, которое, очевидно, будет стоить метод 1 (компилируется N раз, в зависимости от движка JS) с методом 2 (компилируется один раз). Я не могу себе представить разработчика JS, который бы так проходил свой код написания пробации.

Гораздо более реалистичным подходом является анонимное присваивание, так как на самом деле вы используете метод document.onclick больше похож на следующий, который на самом деле мягко предпочитает метод anon.

Используя тестовую среду, подобную вашей:


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}
0
annakata

@nickf

(Жаль, что у меня не было представителя, чтобы просто прокомментировать, но я только что нашел этот сайт)

Моя точка зрения в том, что здесь есть путаница между именованными/анонимными функциями и сценарием выполнения + компиляции в итерации. Как я проиллюстрировал, разница между anon + named незначительна сама по себе - я говорю, что это вариант использования, который неисправен.

Это кажется очевидным для меня, но если нет, то я думаю, что лучший совет - «не делайте глупостей» (из которых одно постоянное смещение блоков + создание объектов в этом сценарии использования), и если вы не уверены, тестируйте!

0
annakata

ДА! Анонимные функции быстрее, чем обычные функции. Возможно, если скорость имеет первостепенное значение ... более важно, чем повторное использование кода, подумайте об использовании анонимных функций.

Здесь есть действительно хорошая статья об оптимизации javascript и анонимных функций:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

0
Christopher Tokar

ссылка почти всегда будет медленнее, чем ссылка. Подумайте об этом так - допустим, вы хотите напечатать результат добавления 1 + 1. Что имеет больше смысла:

alert(1 + 1);

или же

a = 1;
b = 1;
alert(a + b);

Я понимаю, что это действительно упрощенный способ взглянуть на это, но это иллюстративно, верно? Используйте ссылку, только если она будет использоваться несколько раз - например, какой из этих примеров имеет больше смысла:

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

или же

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

Второй - лучшая практика, даже если в ней больше строк. Надеюсь, все это полезно. (и синтаксис jquery никого не отбрасывал)

0
matt lohkamp

Как указано в комментариях к ответу @nickf: Ответ на 

Создает ли функцию один раз быстрее, чем миллион раз

это просто да. Но, как показывает его опыт JS, он не медленнее в миллион раз, показывая, что со временем он действительно становится быстрее.

Более интересный вопрос для меня:

Как повторяется create + run по сравнению с созданием один раз + repeat run.

Если функция выполняет сложное вычисление, время создания объекта функции, скорее всего, незначительно. Но как насчет использования create в случаях, когда run работает быстро? Например:

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

Это JS Perf показывает, что создание функции всего один раз происходит быстрее, чем ожидалось. Однако даже при очень быстрой операции, такой как простое добавление, затраты на повторное создание функции составляют всего несколько процентов.

Различие, вероятно, становится значительным только в тех случаях, когда создание объекта функции является сложным при сохранении незначительного времени выполнения, например, если все тело функции заключено в if (unlikelyCondition) { ... }.

0
bluenote10

То, что определенно ускорит ваш цикл в различных браузерах, особенно в браузерах IE, заключается в следующем:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

Вы поместили произвольную 1000 в состояние цикла, но вы получите мой дрейф, если хотите просмотреть все элементы в массиве.

0
Sarhanis