2013-02-14 3 views
5

Оптимизация кода здесь указана здесь в SO, что профилирование является первым шагом для оптимизации javascript, а предлагаемые двигатели являются профилировщиками Chrome и Firefox. Проблема в том, что они рассказывают каким-то странным образом о времени выполнения каждой функции, но у меня нет хорошего понимания их. Самый полезный способ заключается в том, что профилировщик расскажет, сколько раз каждая строка выполняется и если когда-либо возможно, время, затрачиваемое на каждую строку. Таким образом, можно было бы увидеть узкие места строго. Но до того, как такой инструмент будет реализован/найден, у нас есть два варианта:Производительность присвоения значений массиву

1) сделать собственный калькулятор, который учитывает как время, так и сколько раз выполняется определенный код или строка. 2) научиться понимать, какие медленные методы и которые не являются

Для опции 2 jsperf.com очень полезна. Я попытался изучить оптимизирующие массивы и сделал тест скорости в JSPERF.COM. На следующем рисунке показаны результаты в 5 основных браузерах и обнаружены узкие места, которые я раньше не знал.

Speed test

Основные выводы были:

1) Присвоение значений массивов значительно медленнее, чем присваивание нормальных величин несмотря на какой метод используется для назначения.

2) Preinitializing и/или автозаполнения массив перед выполнением критических циклов может улучшить скорость значительно

3) Математические тригонометрические функции не так медленно, по сравнению с толкая значения в массивы (!)

Вот в объяснения каждого теста:


1. non_array (100%):

Переменные получили предопределенное значение таким образом:

var non_array_0=0; 
var non_array_1=0; 
var non_array_2=0; 
... 

и в определенной временной области они были названы так:

non_array_0=0; 
non_array_1=1; 
non_array_2=2; 
non_array_3=3; 
non_array_4=4; 
non_array_5=5; 
non_array_6=6; 
non_array_7=7; 
non_array_8=8; 
non_array_9=9; 

выше представляет собой массив типа переменной, но там, кажется, нет способа итерации или ссылки на эти переменные другим способом как oppocite для массива. Или есть?

Ничего в этом тесте быстрее, чем присвоение числа переменной.


2. non_array_non_pre (83,78%)

Точно так же, как и в тесте 1, но переменные не были предварительно инициализирован, ни предварительно заполненные. Скорость составляет 83,78% от скорости теста 1. В каждом тестируемом браузере скорость предварительно заполненных переменных была быстрее, чем не заполнялась. Так инициализируйте (и, возможно, префикс) переменные за пределами критических циклов скорости.

Тест код здесь:

var non_array_non_pre_0=0; 
var non_array_non_pre_1=0; 
var non_array_non_pre_2=0; 
var non_array_non_pre_3=0; 
var non_array_non_pre_4=0; 
var non_array_non_pre_5=0; 
var non_array_non_pre_6=0; 
var non_array_non_pre_7=0; 
var non_array_non_pre_8=0; 
var non_array_non_pre_9=0; 

3. pre_filled_array (19,96%):

Массивы зло! Когда мы выбрасываем обычные переменные (test1 и test2) и берем массивы в изображение, скорость значительно уменьшается. Хотя мы делаем все оптимизации (preinitialize и prefill arrays), а затем присваиваем значения напрямую без циклирования или нажатия, скорость уменьшается до 19,96 процента. Это очень грустно, и я действительно не понимаю, почему это происходит. Это было одним из главных потрясений для меня в этом тесте. Массивы так важны, и я не нашел способ сделать много вещей без массивов.

Данные теста здесь:

pre_filled_array[0]=0; 
pre_filled_array[1]=1; 
pre_filled_array[2]=2; 
pre_filled_array[3]=3; 
pre_filled_array[4]=4; 
pre_filled_array[5]=5; 
pre_filled_array[6]=6; 
pre_filled_array[7]=7; 
pre_filled_array[8]=8; 
pre_filled_array[9]=9; 

4. non_pre_filled_array (8,34%):

Это тот же самый тест, как 3, но члены массива не preinitialized, ни предварительно заполненная, только оптимизация была для инициализации массива заранее: var non_pre_filled_array=[];

Скорость уменьшается на 58,23% по сравнению с preinitilized test 3. Таким образом, preinitializing и/или prefilling массив превышают скорость.

Тест код здесь:

non_pre_filled_array[0]=0; 
non_pre_filled_array[1]=1; 
non_pre_filled_array[2]=2; 
non_pre_filled_array[3]=3; 
non_pre_filled_array[4]=4; 
non_pre_filled_array[5]=5; 
non_pre_filled_array[6]=6; 
non_pre_filled_array[7]=7; 
non_pre_filled_array[8]=8; 
non_pre_filled_array[9]=9; 

5. pre_filled_array [I] (7,10%):

Затем к петлям. Самый быстрый метод циклирования в этом тесте. Массив был предварительно инициализирован и предварительно заполнен.

Снижение скорости по сравнению с встроенной версией (тест 3) составляет 64,44%. Это настолько замечательная разница, что я бы сказал, не зацикливайте, если не нужно. Если размер массива мал (не знаю, насколько он мал, его нужно тестировать отдельно), использование встроенных назначений вместо циклов более разумно.

И поскольку падение скорости настолько велико, и нам действительно нужны петли, разумно найти better looping method (например, while(i--)).

Тест код здесь:

for(var i=0;i<10;i++) 
{ 
    pre_filled_array[i]=i; 
} 

6. non_pre_filled_array [я] (5,26%):

Если мы не preinitialize и предварительного заполнения массива, скорость уменьшается 25 , 96%. Опять же, предварительная инициализация и/или предварительное заполнение до критических циклов скорости являются разумными.

Код здесь:

for(var i=0;i<10;i++) 
{ 
    non_pre_filled_array[i]=i; 
} 

7. Расчеты Math (1,17%):

Каждый тест должен быть какой-то точкой отсчета. Математические функции считаются медленными. Тест состоял из десяти «тяжелых» вычислений по математике, но теперь приходит другая вещь, которая поразила меня в этом тесте. Посмотрите на скорость 8 и 9, где мы нажимаем десять целых чисел на массив в цикле. Вычисление этих 10 математических функций более чем на 30% быстрее, чем нажатие десяти целых чисел на массив в цикле. Таким образом, может быть проще преобразовать некоторые массивные нажатия в preinitialized non-arrays и сохранить эти тригонометрии. Конечно, если есть сто или тысячи вычислений на кадр, целесообразно использовать, например. sqrt вместо sin/cos/tan и использовать расстояния для таксисабе для сравнения расстояний и diamond angles (t-radians) for angle comparisons, но все же основным узким местом может быть где-то еще: цикл медленнее, чем наложение, толкание происходит медленнее, чем использование прямого назначения с предварительной инициализацией и/или предварительным заполнением, логикой кода, алгоритмы рисования и доступ к DOM могут быть медленными. Все не могут быть оптимизированы в Javascript (мы должны что-то увидеть на экране!), Но все, что легко и важно, мы можем сделать, разумно делать. Кто-то здесь, в SO, сказал, что код для людей, а читаемый код более важен, чем быстрый код, потому что стоимость обслуживания является самой большой ценой. Это экономичная точка зрения, но я обнаружил, что оптимизация кода может получить оба: элегантность и читаемость и производительность. И если будет достигнуто повышение производительности на 5%, и код будет более прямым, это даст хорошее чувство!

Код здесь:

non_array_0=Math.sqrt(10435.4557); 
non_array_1=Math.atan2(12345,24869); 
non_array_2=Math.sin(35.345262356547); 
non_array_3=Math.cos(232.43575432); 
non_array_4=Math.tan(325); 
non_array_5=Math.asin(3459.35498534536); 
non_array_6=Math.acos(3452.35); 
non_array_7=Math.atan(34.346); 
non_array_8=Math.pow(234,222); 
non_array_9=9374.34524/342734.255; 

8. pre_filled_array.push (я) (0,8%):

Нажмите зло! Толчок в сочетании с петлей - зловещее зло! Это по какой-то причине очень медленный метод для назначения значений в массив. Тест 5 (прямые назначения в цикле) почти в 9 раз быстрее, чем этот метод, и оба метода делают одно и то же: присвойте целое число 0-9 в preinitialized и предварительно заполненный массив. Я не тестировал, если это злобное зрелище «за тобой» происходит из-за нажатия или циклирования или комбинации обоих или подсчета циклов. В JSPERF.COM есть другие примеры, которые дают противоречивые результаты. Разумно проверять только фактические данные и принимать решения. Этот тест может быть несовместим с другими данными, кроме того, что было использовано.

А вот код:

for(var i=0;i<10;i++) 
{ 
    pre_filled_array.push(i); 
} 

9. non_pre_filled_array.push (я) (0,74%):

Последний и самый медленный метод в этом тесте то же самое как тест 8, но массив не заполняется заранее. Немного медленнее 9, но разница незначительна (7,23%). Но давайте возьмем пример и сравним этот самый медленный метод с самым быстрым. Скорость этого метода составляет 0,74% от скорости метода 1, что означает, что метод 1 в 135 раз быстрее этого. Так что подумайте внимательно, если массивы вообще необходимы, в частности, для использования. Если это всего лишь одно или несколько нажатий, общая разница в скорости не заметна, но, с другой стороны, если есть только несколько нажатий, они очень просты и изящны для преобразования в переменные без массива.

Это код:

for(var i=0;i<10;i++) 
{ 
    non_pre_filled_array.push(i); 
} 

И, наконец, на обязательное SO вопроса:

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

Я не могу использовать var variable_$i = 1 в цикле, так что $ i преобразуется в какое-то целое число. Я должен использовать var variable[i] = 1, который значительно медленнее, чем var variable1 = 1, поскольку тест доказан. Это может быть критическим только тогда, когда есть большие массивы, и во многих случаях они есть.


EDIT: Я сделал новый тест, чтобы подтвердить замедленность доступа массивов и пытались найти более быстрый способ:

http://jsperf.com/read-write-array-vs-variable

Массив чтения и/или массив записи значительно медленнее чем при использовании обычных переменных. Если некоторые операции выполняются с элементами массива, разумнее сохранить значение члена массива переменной temp, сделать эти операции переменной temp и, наконец, сохранить значение в члене массива. И хотя код становится больше, значительно быстрее сделать эти операции встроенными, чем в цикле.

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


+0

Какой механизм Javascript? Является ли код таким, что JIT имеет шанс? – vonbrand

+0

Массивы не являются злыми, но они являются объектами, и они делают так много удивительных вещей, о которых вам не нужно беспокоиться. У них есть удобное свойство 'length', а также методы фильтрации/сортировки и т. Д. Если вам не нужны эти функции, вы можете попробовать что-то вроде' window ["varname_" + i] ', чтобы иметь« массив » переменные значения, но похоже, что вы не тестировали это. –

+0

Вы заранее распределяете свои массивы? если это не имеет значения? 'new Array (1000)' –

ответ

3

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

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

Эти оптимизации будут также выполняться для массивов, но они не так просты и потребуют больше времени для вступления в силу. При разрешении ссылки на свойства есть дополнительные накладные расходы, и поскольку массивы JavaScript являются авто-растущими списками, необходимо также проверить длину.

Prepopulating массивов поможет избежать перераспределения для изменения емкости, но для ваших маленьких массивов (length = 10) это не должно иметь большого значения.

Есть ли способ получить скорость не-массива-переменных-настроек и динамику массивов?

№ Динамика действительно стоит, но они того стоят - как и петли.

Вам вряд ли когда-либо понадобится такая микро-оптимизация, don't try it. Единственное, что я могу придумать, это петли фиксированного размера (n < = 4) при работе с ImageData, там применима вставка.

Push is evil!

Нет, только ваш тест был испорчен. Фрагменты jsperf выполняются в синхронизированном цикле без tearup и -down, и только там вы сбросили размер. Ваши повторные push es производят массивы длиной до сотых тысяч, с соответствующими потребностями памяти (перераспределения). См. Консоль на http://jsperf.com/pre-filled-array/11.

Фактически push так же быстро, как и присвоение недвижимости. Хорошие измерения встречаются редко, но те, которые сделаны правильно, показывают разные результаты в разных версиях браузера - быстро и неожиданно. См. How to append something to an array?, Why is array.push sometimes faster than array[n] = value? и Is there a reason JavaScript developers don't use Array.push()? - вывод состоит в том, что вы должны использовать то, что является наиболее читаемым/подходящим для вашего прецедента, а не то, что, по вашему мнению, может быть быстрее.

+0

Я даже не понимаю этот пост ... Это вопрос? Как это получилось 5 раз и привилегировано один раз? Спасибо, что ответили правильно. – 1nfiniti

+0

@mikeyUX: Да, это слишком долго (и раздел расчетов кажется совершенно не связанным), но в глубине есть реальный вопрос. Используйте 'Strg' +' F' для 'Question', чтобы найти его :-) – Bergi

+0

Долго это так, хорошо. Извини за это. У этого есть недостатки (относительно толчка зла, спасибо @ Bergi об обнаружении этого). Измерения выполняются для определения наиболее эффективной обработки переменных для использования в Clipper (http://jsclipper.sourceforge.net/6.2.1.0/main_demo.html). Он использует массивы массивов, заполненных объектами вершин со свойствами X и Y и, возможно, Z. –

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