Может ли привязка переменных вне области ускорить ваш код? - PullRequest
5 голосов
/ 22 июня 2011

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

Пример в простом JS:

var a = 1,
    func1 = function(b) { console.log(a,b); },
    func2 = (function(a,b) { console.log(a,b); }).bind(null, a);

//func1(2) vs func2(2)

Пример в узле

var fs = require('fs'),
    func1 = function(f) { fs.stat(f, function(err, stats){}); },
    func2 = (function(fs, f) { fs.stat(f, function(err, stats){}); }).bind(null, fs);

//func1('file.txt') vs func2('file.txt')

В моих приведенных выше примерах func1 или func2 будут заметно быстрее других (не включая внешние факторы, такие как время, необходимое для получения статистики файла)?


Вот небольшой JSFiddle, который я собрал, который делает быстрый и грязный тест: http://jsfiddle.net/AExvz/

  1. Google Chrome 14.0.797.0 dev-m
    • Func1: 2-4ms
    • Func2: 30-46мс
  2. Google Chrome 14.0.800.0 канарейка
    • Func1: 2-7ms
    • Func2: 35-39мс
  3. Firefox 5.0
    • Func1: 0-1ms
    • Func2: 35-42мс
  4. Opera 11.11 Build 2109
    • Func1: 21-32мс
    • Func2: 68-73мс
  5. Safari 5.05 (7533.21.1)
    • Func1: 23-34мс
    • Func2: 71-78мс
  6. Internet Explorer 9.0.8112.16421
    • Func1: 10-17ms
    • Func2: 14-17мс
  7. Узел 0.4.8 REPL
    • Func1: 10 мс
    • Func2: 156 мс при 10-кратном увеличении числа итераций (~ 15,6 мс при тестировании с 100000 итерациями)

Примечание: Тест узла REPL ненадежен, поскольку он должен использовать какую-то систему кеширования. После одного теста func1 func2 вернул 0 мс.

Не стесняйтесь представить свои результаты лучшего теста.

Ответы [ 3 ]

2 голосов
/ 22 июня 2011

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

Вот тест для демонстрации (оригинальный тест cwolves):

http://jsperf.com/outer-vs-inner-references/2

Настройка:

var x = 10, y = 11, z = 12, z = 13, a = 14, g = 15;

Контрольный пример № 1 (Внешняя ссылка):

(function(){
    for(var i=0; i<1000; i++){
        x + y + z + a + g
    }
})();

Тестовый пример № 2 (локальная ссылка):

(function(x,y,z,a,g){
    for(var i=0; i<1000; i++){
        x + y + z + a + g;
    }
})(x,y,z,a,g);

Результаты :

Согласно этому тесту второй тестовый случай намного быстрее, чемпервый случай.Честно говоря, я был немного удивлен, и мне интересно, если мой собственный тест ошибочен.Я знал, что это будет быстрее, но полагал, что различия будут незначительными - но, видимо, нет?

2 голосов
/ 22 июня 2011

Как правило, эффект сокращения поиска по области должен быть положительным. Однако на современных быстрых двигателях JS разница, скорее всего, незначительна.

В некотором математически интенсивном коде, работающем на более старом движке JS, я привык получать больше информации, выполняя такие вещи:

function doSomething() {
    var round = Math.round;
    var floor = Math.floor;

    //Do something that calls floor and round a lot
}

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

1 голос
/ 22 июня 2011

Основываясь на некоторых тестах, которые я завершил (см. Вопрос), и советах Джани, кажется, что в современных браузерах нового поколения проблемы с областью применения были облегчены с помощью быстрых движков, таких как V8. Теоретически, уменьшение количества просмотров области должно увеличить скорость, но тесты не поддержали это.
Для тех, кто специально работает с Node.JS, кажется, что вам нужно беспокоиться только о первой итерации функции. Когда что-то вызывается в Node несколько раз, создается впечатление, что движок V8 способен кэшировать часть выполнения функции для последующего использования. Чтобы избежать этого кеширования, для func2 было использовано большее количество итераций. Простая математика показала, что после масштабирования теста для func2 он был примерно на 5,6 мс медленнее, чем func1. Учитывая колебания, которые вы можете увидеть в большинстве браузеров, я бы предположил, что оба, вероятно, танцуют вокруг значений между 5 мс и 15 мс. Однако я бы порекомендовал придерживаться метода func1, так как он, кажется, имеет небольшое преимущество и более широко поддерживается (я смотрю на вас IE lt 9).

...