2009-10-10 3 views
25

Я просматривал Google Code, когда я оценил этот проект под названием JSpeed ​​- оптимизация для Javascript.post increment vs pre increment - Оптимизация Javascript

Я заметил, что одна из оптимизаций заключалась в том, чтобы изменить i++ на ++i для операторов цикла.

Перед Optimization

for (i=0;i<1;i++) {} 

for (var i = 0, j = 0; i < 1000000; i++, j++) { 
    if (i == 4) { 
     var tmp = i/2; 
    } 

    if ((i % 2) == 0) { 
     var tmp = i/2; 
     i++; 
    } 
} 
var arr = new Array(1000000); 
for (i = 0; i < arr.length; i++) {} 

После оптимизации

for(var i=0;i<1;++i){} 
for(var i=0,j=0;i<1000000;++i,++j){if(i==4){var tmp=i>>1;} 
if((i&1)==0){var tmp=i>>1;i++;}} 
var arr=new Array(1000000);for(var i=0,arr_len=arr.length;i<arr_len;++i){} 

Я знаю, что до и приросты почтовые делать, но любая идея, как это ускоряет код вверх?

+76

Подходит ли оптимизация к сжатию всего кода, чтобы сделать его нечитаемым? Genius! – ChaosPandion

+1

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

+7

С каких пор парсер не нуждается в распаковке? Оптимизация здесь - транспорт, а не производительность. –

ответ

41

Это то, что я прочитал и могу ответить на ваш вопрос: «прединкремент (++i) прибавляет к значению i, затем возвращает i, в отличии от этого, i++ возвращает i затем добавляет один к нему , что в теории приводит к созданию временной переменной, сохраняющей значение i до того, как была применена операция приращения ".

+1

возможно теория. любые цитаты или ссылки, чтобы читать больше? – mauris

+0

Он исходил из: http://physical-thought.blogspot.com/2008/11/pre-vs-post-increment-speed-test.html. Насколько я понимаю, практика может быть различной для каждого компилятора. Кстати: через http://home.earthlink.net/~kendrasg/info/js_opt/ вы можете узнать больше о оптимизации javascript. – KooiInc

+0

Hi Kooilnc - yep увидел, что сообщение в блоге от googling. большое спасибо. – mauris

2

Оптимизация не является префиксом по сравнению с постом. Это использование побитовых операторов «shift» и «and», а не деление и мода.

Существует также оптимизация минимизации javascript для уменьшения общего размера (но это не оптимизация времени исполнения).

+1

Есть некоторые доказательства того, что pre vs. post действительно имеет значение ... в зависимости от двигателя. – Glenn

+0

Можете ли вы предоставить источник? Это не имеет большого значения для меня. –

+0

Я знаю, что есть и другие оптимизации. но если это не считается частью оптимизации, то почему JSpeed ​​беспокоит включение этой изменяющейся записи в pre increment? – mauris

3

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

http://blogs.oracle.com/greimer/entry/best_way_to_code_a

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

Редактировать: В соответствии с this guy pre vs. post статистически незначителен. (С предварительно возможно быть хуже)

+0

Это скорее часть инкремента, чем способ доступа к массивам. Я знаю, как 'for (i = 0; i mauris

+1

Я ничего не вижу в вашей ссылке , который обсуждает дов. –

+0

Ха! Я слепой. В моей ссылке нет предварительной записи. Проверка правильной ссылки сейчас. – Glenn

0

Только что протестировали его в firebug и не обнаружили разницы между пост-и преинкрементами. Может быть, эта оптимизация других платформ? Вот мой код для тестирования Firebug:

function test_post() { 
    console.time('postIncrement'); 
    var i = 1000000, x = 0; 
    do x++; while(i--); 
    console.timeEnd('postIncrement'); 
} 

function test_pre() { 
    console.time('preIncrement'); 
    var i = 1000000, x = 0; 
    do ++x; while(i--); 
    console.timeEnd('preIncrement'); 
} 

test_post(); 
test_pre(); 
test_post(); 
test_pre(); 
test_post(); 
test_pre(); 
test_post(); 
test_pre(); 

Выход:

postIncrement: 140ms 
preIncrement: 160ms 
postIncrement: 136ms 
preIncrement: 157ms 
postIncrement: 148ms 
preIncrement: 137ms 
postIncrement: 136ms 
preIncrement: 148ms 
+0

Я уже сделал тест на firefox. не имеет большого разницы. теория, данная на другом ответе, может быть только ответом. Спасибо за попытку! – mauris

+0

Кому нужна скорость. Если вы не используете JavaScript для zillions, это не будет означать конечный пользователь. –

+0

@mP - согласовано.но некоторые браузеры * coughIE * ... = D – mauris

50

Это лже оптимизация. Насколько я понимаю, вы сохраняете 1 op-код. Если вы хотите оптимизировать свой код с помощью этой техники, то вы пошли не так. Кроме того, большинство компиляторов/переводчиков в любом случае оптимизируют это для вас (reference 1). Короче, я бы не волновался. Но, если вы действительно волнуетесь, вы должны использовать i+=1.

Вот быстрый и грязный тест я просто сделал

var MAX = 1000000, t=0,i=0; 

t = (new Date()).getTime(); 
for (i=0; i<MAX;i++) {} 
t = (new Date()).getTime() - t; 

console.log(t); 

t = (new Date()).getTime(); 
for (i=0; i<MAX;++i) {} 
t = (new Date()).getTime() - t; 

console.log(t); 

t = (new Date()).getTime(); 
for (i=0; i<MAX;i+=1) {} 
t = (new Date()).getTime() - t; 

console.log(t); 

Сырые результаты

Post Pre  += 
1071 1073 1060 
1065 1048 1051 
1070 1065 1060 
1090 1070 1060 
1070 1063 1068 
1066 1060 1064 
1053 1063 1054 

Удалены низкий и высокий

Post Pre  += 
1071 ---- 1060 
1065 ---- ---- 
1070 1065 1060 
---- 1070 1060 
1070 1063 ---- 
1066 1060 1064 
---- 1063 1054 

A verages

1068.4 1064.2 1059.6 

Обратите внимание, что это более один миллион итераций и результаты находятся в пределах миллисекунд в среднем. Не очень большая оптимизация, учитывая, что большая итеративная обработка в JavaScript выполняется на гораздо меньших наборах (например, контейнеры DOM).

+0

согласился Justin. 15char – mauris

+0

Что вы подразумеваете под 15char? –

+0

, чтобы заполнить минимум 15 символов. во всяком случае, 1 op * n итераций может быть много. – mauris

0

Это, вероятно, грузо-культовое программирование. Это не должно иметь значения, если вы используете достойные компиляторы/интерпретаторы для языков, которые не имеют произвольной перегрузки оператора.

Эта оптимизация имеет смысл для C++, где

T x = ...; 
++x 

может изменить значение в месте, тогда как

T x = ...; 
x++ 

бы создать копию, делая что-то под капотом, как

T x = ...; 
T copy; 
(copy = T(x), ++x, copy) 

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

1

тест Anatoliy включал пост-инкремент внутри испытательной Преинкремент функции :(

Вот результаты без этого побочного эффекта ...

function test_post() { 
    console.time('postIncrement'); 
    var i = 1000000, x = 0; 
    do x++; while(i--); 
    console.timeEnd('postIncrement'); 
} 

function test_pre() { 
    console.time('preIncrement'); 
    var i = 1000000, x = 0; 
    do ++x; while(--i); 
    console.timeEnd('preIncrement'); 
} 

test_post(); 
test_pre(); 
test_post(); 
test_pre(); 
test_post(); 
test_pre(); 
test_post(); 
test_pre(); 

Выход

postIncrement: 3.21ms 
preIncrement: 2.4ms 
postIncrement: 3.03ms 
preIncrement: 2.3ms 
postIncrement: 2.53ms 
preIncrement: 1.93ms 
postIncrement: 2.54ms 
preIncrement: 1.9ms 

Это большая разница.

+0

Я думаю, что причина в том, что они различны, потому что 'while (i -)' должен сохранять значение 'i', затем декремент' i', затем проверьте предыдущее значение 'i', чтобы решить, закончен ли цикл. 'while (- i)' не должен выполнять эту дополнительную работу. Очень необычно использовать 'i -' или 'i ++' в условном тесте. Конечно, в операции приращения оператора 'for', но не в условном тесте. –

+0

Когда вы используете '--i', вы должны установить его на' 1000001', потому что он закончится раньше :) Но, конечно, это не большая разница. –

1

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

Например, давайте рассмотрим этот образец кода:

sh$ cat test.js 
function preInc(){ 
    for(i=0; i < 10; ++i) 
    console.log(i); 
} 

function postInc(){ 
    for(i=0; i < 10; i++) 
    console.log(i); 
} 

// force lazy compilation 
preInc(); 
postInc(); 

В этом случае V8 компилятор NodeJS производит точно тот же байт-код (смотрите особ.в опкодах 39-44 для приращения):

sh$ node --version 
v8.9.4 
sh$ node --print-bytecode test.js | sed -nEe '/(pre|post)Inc/,/^\[/p' 
[generating bytecode for function: preInc] 
Parameter count 1 
Frame size 24 
    77 E> 0x1d4ea44cdad6 @ 0 : 91    StackCheck 
    87 S> 0x1d4ea44cdad7 @ 1 : 02    LdaZero 
    88 E> 0x1d4ea44cdad8 @ 2 : 0c 00 03   StaGlobalSloppy [0], [3] 
    94 S> 0x1d4ea44cdadb @ 5 : 0a 00 05   LdaGlobal [0], [5] 
     0x1d4ea44cdade @ 8 : 1e fa    Star r0 
     0x1d4ea44cdae0 @ 10 : 03 0a    LdaSmi [10] 
    94 E> 0x1d4ea44cdae2 @ 12 : 5b fa 07   TestLessThan r0, [7] 
     0x1d4ea44cdae5 @ 15 : 86 23    JumpIfFalse [35] (0x1d4ea44cdb08 @ 50) 
    83 E> 0x1d4ea44cdae7 @ 17 : 91    StackCheck 
    109 S> 0x1d4ea44cdae8 @ 18 : 0a 01 0d   LdaGlobal [1], [13] 
     0x1d4ea44cdaeb @ 21 : 1e f9    Star r1 
    117 E> 0x1d4ea44cdaed @ 23 : 20 f9 02 0f  LdaNamedProperty r1, [2], [15] 
     0x1d4ea44cdaf1 @ 27 : 1e fa    Star r0 
    121 E> 0x1d4ea44cdaf3 @ 29 : 0a 00 05   LdaGlobal [0], [5] 
     0x1d4ea44cdaf6 @ 32 : 1e f8    Star r2 
    117 E> 0x1d4ea44cdaf8 @ 34 : 4c fa f9 f8 0b CallProperty1 r0, r1, r2, [11] 
    102 S> 0x1d4ea44cdafd @ 39 : 0a 00 05   LdaGlobal [0], [5] 
     0x1d4ea44cdb00 @ 42 : 41 0a    Inc [10] 
    102 E> 0x1d4ea44cdb02 @ 44 : 0c 00 08   StaGlobalSloppy [0], [8] 
     0x1d4ea44cdb05 @ 47 : 77 2a 00   JumpLoop [42], [0] (0x1d4ea44cdadb @ 5) 
     0x1d4ea44cdb08 @ 50 : 04    LdaUndefined 
    125 S> 0x1d4ea44cdb09 @ 51 : 95    Return 
Constant pool (size = 3) 
Handler Table (size = 16) 
[generating bytecode for function: get] 
[generating bytecode for function: postInc] 
Parameter count 1 
Frame size 24 
    144 E> 0x1d4ea44d821e @ 0 : 91    StackCheck 
    154 S> 0x1d4ea44d821f @ 1 : 02    LdaZero 
    155 E> 0x1d4ea44d8220 @ 2 : 0c 00 03   StaGlobalSloppy [0], [3] 
    161 S> 0x1d4ea44d8223 @ 5 : 0a 00 05   LdaGlobal [0], [5] 
     0x1d4ea44d8226 @ 8 : 1e fa    Star r0 
     0x1d4ea44d8228 @ 10 : 03 0a    LdaSmi [10] 
    161 E> 0x1d4ea44d822a @ 12 : 5b fa 07   TestLessThan r0, [7] 
     0x1d4ea44d822d @ 15 : 86 23    JumpIfFalse [35] (0x1d4ea44d8250 @ 50) 
    150 E> 0x1d4ea44d822f @ 17 : 91    StackCheck 
    176 S> 0x1d4ea44d8230 @ 18 : 0a 01 0d   LdaGlobal [1], [13] 
     0x1d4ea44d8233 @ 21 : 1e f9    Star r1 
    184 E> 0x1d4ea44d8235 @ 23 : 20 f9 02 0f  LdaNamedProperty r1, [2], [15] 
     0x1d4ea44d8239 @ 27 : 1e fa    Star r0 
    188 E> 0x1d4ea44d823b @ 29 : 0a 00 05   LdaGlobal [0], [5] 
     0x1d4ea44d823e @ 32 : 1e f8    Star r2 
    184 E> 0x1d4ea44d8240 @ 34 : 4c fa f9 f8 0b CallProperty1 r0, r1, r2, [11] 
    168 S> 0x1d4ea44d8245 @ 39 : 0a 00 05   LdaGlobal [0], [5] 
     0x1d4ea44d8248 @ 42 : 41 0a    Inc [10] 
    168 E> 0x1d4ea44d824a @ 44 : 0c 00 08   StaGlobalSloppy [0], [8] 
     0x1d4ea44d824d @ 47 : 77 2a 00   JumpLoop [42], [0] (0x1d4ea44d8223 @ 5) 
     0x1d4ea44d8250 @ 50 : 04    LdaUndefined 
    192 S> 0x1d4ea44d8251 @ 51 : 95    Return 
Constant pool (size = 3) 
Handler Table (size = 16) 

Конечно, другие JavaScript компиляторов/переводчики может сделать иначе, но это сомнительно.

В качестве последнего слова, за то, что он стоит, я все-таки считаю наилучшей практикой использовать Преинкремент, когда это возможно: с тех пор я часто переключать языки, я предпочитаю использовать синтаксис с правильным семантического за то, что я хочу , вместо того, чтобы полагаться на умение компилятора. Например, современные компиляторы C тоже не будут иметь никакого значения. Но в C++ это может оказать существенное влияние на перегруженный operator++.

Смежные вопросы