Разве локальные функции не являются микрооптимизацией? - PullRequest
2 голосов
/ 20 января 2011

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

В этом конкретном случае функция doMoreStuff используется только внутри doStuff,Должен ли я беспокоиться о таких локальных функциях?

function doStuff() {
    var doMoreStuff = function(val) {
         // do some stuff
    }

    // do something
    for (var i = 0; i < list.length; i++) {
         doMoreStuff(list[i]);
         for (var  j = 0; j < list[i].children.length; j++) {
              doMoreStuff(list[i].children[j]);
         }
    }
    // do some other stuff

}

В качестве примера можно привести:

function sendDataToServer(data) {
    var callback = function(incoming) {
         // handle incoming
    }

    ajaxCall("url", data, callback);

} 

Ответы [ 5 ]

4 голосов
/ 20 января 2011

Не уверен, относится ли это к категории «микрооптимизация». Я бы сказал нет.

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

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

var doStuff = (function() {
    var doMoreStuff = function(val) {
         // do some stuff
    }
    return function() {
        // do something
        for (var i = 0; i < list.length; i++) {
            doMoreStuff(list[i]);
        }
        // do some other stuff 
    }
}());

Поскольку возвращаемая функция является замыканием, она имеет доступ к doMoreStuff. Обратите внимание, что внешняя функция выполняется немедленно ((function(){...}())).

Или вы создаете объект, который содержит ссылки на функции:

var stuff = {
    doMoreStuff: function() {...},
    doStuff: function() {...}
};

Более подробную информацию об инкапсуляции, шаблонах создания объектов и других концепциях можно найти в книге Шаблоны JavaScript .

0 голосов
/ 26 октября 2015

Оригинальный вопрос был задан в 2011 году. Учитывая рост Node.js с тех пор, я подумал, что стоит вернуться к этой проблеме. В серверной среде несколько миллисекунд могут иметь большое значение. Это может быть разница между тем, чтобы оставаться отзывчивым под нагрузкой или нет.

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

function a1(n) {
    return n + 2;
}

function a2(n) {
    return 2 - n;
}

function a() {
    var k = 5;
    for (var i = 0; i < 100000000; i++) {
        k = a1(k) + a2(k);
    }
    return k;
}

function b() {
    function b1(n) {
        return n + 2;
    }

    function b2(n) {
        return 2 - n;
    }

    var k = 5;
    for (var i = 0; i < 100000000; i++) {
        k = b1(k) + b2(k);
    }
    return k;
}

function measure(label, fn) {
    var s = new Date();
    var r = fn();
    var e = new Date();
    console.log(label, e - s);
}

for (var i = 0; i < 4; i++) {
    measure('A', a);
    measure('B', b);
}

Команда для запуска кода:

node --trace_deopt test.js

Выход:

[deoptimize global object @ 0x2431b35106e9]
A 128
B 130
A 132
[deoptimizing (DEOPT eager): begin 0x3ee3d709a821 b (opt #5) @4, FP to SP delta: 72]
  translating b => node=36, height=32
    0x7fffb88a9960: [top + 64] <- 0x2431b3504121 ; rdi 0x2431b3504121 <undefined>
    0x7fffb88a9958: [top + 56] <- 0x17210dea8376 ; caller's pc
    0x7fffb88a9950: [top + 48] <- 0x7fffb88a9998 ; caller's fp
    0x7fffb88a9948: [top + 40] <- 0x3ee3d709a709; context
    0x7fffb88a9940: [top + 32] <- 0x3ee3d709a821; function
    0x7fffb88a9938: [top + 24] <- 0x3ee3d70efa71 ; rcx 0x3ee3d70efa71 <JS Function b1 (SharedFunctionInfo 0x361602434ae1)>
    0x7fffb88a9930: [top + 16] <- 0x3ee3d70efab9 ; rdx 0x3ee3d70efab9 <JS Function b2 (SharedFunctionInfo 0x361602434b71)>
    0x7fffb88a9928: [top + 8] <- 5 ; rbx (smi)
    0x7fffb88a9920: [top + 0] <- 0 ; rax (smi)
[deoptimizing (eager): end 0x3ee3d709a821 b @4 => node=36, pc=0x17210dec9129, state=NO_REGISTERS, alignment=no padding, took 0.203 ms]
[removing optimized code for: b]
B 1000
A 125
B 1032
A 132
B 1033

Как видите, изначально функции A и B работали с одинаковой скоростью. Затем по какой-то причине произошло событие деоптимизации. С тех пор B почти на порядок медленнее.

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

0 голосов
/ 20 января 2011

Быстрый «тест» запускается на среднем ПК (я знаю, что есть много неучтенных переменных, поэтому не комментируйте очевидное, но в любом случае это интересно):

count = 0;
t1 = +new Date();
while(count < 1000000) {
  p = function(){};
  ++count;
}
t2 = +new Date();
console.log(t2-t1); // milliseconds

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

Запуск 3 раза дал:

913
878
890

Затем закомментируйте строку создания функции, 3 прогона дали:

462
458
464

Таким образом, на 1000 000 пустых созданий функций вы добавляете около половины секунды. Даже если предположить, что ваш оригинальный код выполняется на портативном устройстве 10 раз в секунду (скажем, общая производительность устройств составляет 1/100 от этого ноутбука, что преувеличено - вероятно, ближе к 1/10, хотя и обеспечит хорошую верхнюю границу) , это эквивалентно 1000 созданиям функций в секунду на этом компьютере, что происходит за 1/2000 секунды. Таким образом, каждую секунду портативное устройство добавляет дополнительную нагрузку на 1/2000 секунды обработки ... полмисекунды в секунду не очень много.

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

0 голосов
/ 20 января 2011

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

Оптимизация возможна только тогда, когда у вас работает что-то слишком медленное (если оно еще не работает, оптимизировать рано. Период). Помните, никто никогда не платил дополнительно за программу, которая была быстрее, чем их потребности / требования ...

Редактировать : Учитывая, что программа еще не закончена, это также преждевременная оптимизация. Почему это плохо? Ну, во-первых, вы проводите время, работая над чем-то, что может не иметь значения в долгосрочной перспективе. Во-вторых, у вас нет базовой линии, чтобы увидеть, улучшили ли ваши оптимизации что-либо в реалистическом смысле. В-третьих, вы снижаете удобство сопровождения и читаемость еще до того, как вы его запустите, поэтому запускать его будет сложнее, чем если бы вы использовали чистый лаконичный код. В-четвертых, вы не знаете, понадобится ли вам doMoreStuff где-то еще в программе, пока вы не закончите ее и не поймете все свои потребности (возможно, в общих чертах, в зависимости от точных деталей, но не вне области возможного).

Есть причина, по которой Донналд Кнут сказал: Преждевременная оптимизация - корень всего зла ...

0 голосов
/ 20 января 2011

Это полностью зависит от того, как часто вызывается функция.Если это функция OnUpdate, которая вызывается 10 раз в секунду, это приличная оптимизация.Если он вызывается три раза на страницу, это микрооптимализация.

Хотя удобные определения вложенных функций не нужны (их можно заменить дополнительными аргументами для функции).

Примервложенная функция:

function somefunc() {
    var localvar = 5

    var otherfunc = function() {
         alert(localvar);
    }

    otherfunc();
}

То же самое, теперь вместо аргумента:

function otherfunc(localvar) {
    alert(localvar);
}

function somefunc() {
    var localvar = 5

    otherfunc(localvar);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...