Для VS Foreach по производительности массива (в AS3 / Flex) - PullRequest
16 голосов
/ 18 июня 2009

Какой из них быстрее? Почему?

var messages:Array = [.....]

// 1 - for
var len:int = messages.length;
for (var i:int = 0; i < len; i++) {
    var o:Object = messages[i];
    // ...
}

// 2 - foreach
for each (var o:Object in messages) {
    // ...
}

Ответы [ 8 ]

23 голосов
/ 21 июня 2009

С того места, где я сижу, обычные петли for умеренно быстрее, чем for each петли в минимальном случае. Кроме того, как и в дни AS2, уменьшение вашего цикла через for обычно дает очень незначительное улучшение.

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

Пример кода:

var size:Number = 10000000;
var arr:Array = [];
for (var i:int=0; i<size; i++) { arr[i] = i; }
var time:Number, o:Object;

// for()
time = getTimer();
for (i=0; i<size; i++) { arr[i]; }
trace("for test: "+(getTimer()-time)+"ms");

// for() reversed
time = getTimer();
for (i=size-1; i>=0; i--) { arr[i]; }
trace("for reversed test: "+(getTimer()-time)+"ms");

// for..in
time = getTimer();
for each(o in arr) { o; }
trace("for each test: "+(getTimer()-time)+"ms");

Результаты:

for test: 124ms
for reversed test: 110ms
for each test: 261ms

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

Редактировать 2: Ответы на комментарий ошышко:

  1. Компилятор может пропускать доступы в моих внутренних циклах, но это не так. Циклы выходили бы в два или три раза быстрее, если бы это было.
  2. Результаты изменяются в примере кода, который вы разместили, потому что в этой версии цикл for теперь имеет неявное преобразование типов. Я оставил задания из своих циклов, чтобы избежать этого. Конечно, можно утверждать, что в цикле for вполне нормально иметь дополнительное приведение, потому что «реальному коду» оно все равно понадобится, но для меня это просто еще один способ сказать: «нет общего ответа; что вы делаете внутри своей петли ". Какой ответ я тебе даю. ;)
2 голосов
/ 22 июня 2009

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

var array:Array=new Array();
for (var k:uint=0; k<1000000; k++) {
    array.push(Math.random());
}

stage.addEventListener("mouseDown",foreachloop);
stage.addEventListener("mouseUp",forloop);

/////// Array /////

/* 49ms */
function foreachloop(e) {
    var t1:uint=getTimer();
    var tmp:Number=0;
    var i:uint=0;
    for each (var n:Number in array) {
        i++;
        tmp+=n;
    }
    trace("foreach", i, tmp, getTimer() - t1);
}
/***** 81ms  ****/
function forloop(e) {
    var t1:uint=getTimer();
    var tmp:Number=0;
    var l:uint=array.length;
    for(var i:uint = 0; i < l; i++)
        tmp += Number(array[i]);
    trace("for", i, tmp, getTimer() - t1);
}

Что мне нравится в этих тестах, так это то, что у вас есть ссылка и на ключ, и на значение в каждой итерации обоих циклов (удаление счетчика ключей в цикле «для каждого» не имеет значения). Кроме того, он работает с Number, который, вероятно, является наиболее распространенным циклом, который вы захотите оптимизировать. И самое главное, победителем является «для каждого», что является моей любимой петлей: P

Примечания:

-Ссылка на массив в локальной переменной внутри функции цикла for-each не имеет значения, но в цикле for вы получаете ускорение (75 мс вместо 105 мс):

function forloop(e) {
    var t1:uint=getTimer();
    var tmp:Number=0;
    var a:Array=array;
    var l:uint=a.length;
    for(var i:uint = 0; i < l; i++)
        tmp += Number(a[i]);
    trace("for", i, tmp, getTimer() - t1);
}

-Если вы выполняете те же тесты с классом Vector, результаты немного сбивают с толку: S

2 голосов
/ 21 июня 2009

При выполнении итерации по массиву каждый цикл выполняется быстрее в моих тестах.

var len:int = 1000000;
var i:int = 0;
var arr:Array = [];

while(i < len) {
    arr[i] = i;
    i++;
}

function forEachLoop():void {
    var t:Number = getTimer();
    var sum:Number = 0;
    for each(var num:Number in arr) {
        sum += num;
    }
    trace("forEachLoop :", (getTimer() - t));
}

function whileLoop():void {
    var t:Number = getTimer();
    var sum:Number = 0;
    var i:int = 0;
    while(i < len) {
        sum += arr[i] as Number;                
        i++;
    }
    trace("whileLoop :", (getTimer() - t));
}

forEachLoop();
whileLoop();

Это дает:

forEachLoop: 87 whileLoop: 967

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

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

Обратите внимание, однако, что если преобразование типов действительно имеет место, то для каждой версии намного медленнее, а версия while заметно быстрее (хотя, тем не менее, для каждой доли в то время):

Чтобы проверить, измените инициализацию массива на это:

while(i < len) {
    arr[i] = i + "";
    i++;
}

А теперь результаты:

forEachLoop: 328 whileLoop: 366

forEachLoop: 324 whileLoop: 369

1 голос
/ 18 июня 2009

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

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

0 голосов
/ 01 октября 2012

ребята! Особенно Хуан Пабло Калифано . Я проверил твой тест. Основное отличие в получении элемента массива. Если вы поставите var len : int = 40000;, вы увидите, что цикл «пока» быстрее. Но он проигрывает с большим количеством массивов, а не для ... каждого.

0 голосов
/ 16 декабря 2009

Возможно, в массиве, где все элементы находятся там и начинаются с нуля (от 0 до X), было бы быстрее использовать цикл for. Во всех других случаях (разреженный массив) это может быть ОЧЕНЬ быстрее, чтобы использовать для каждого. Причина заключается в использовании двух структур данных в массиве: таблица Hast и массив Debse. Пожалуйста, прочитайте мой анализ массива с использованием источника Тамарин: http://jpauclair.wordpress.com/2009/12/02/tamarin-part-i-as3-array/

Цикл for будет проверять по неопределенному индексу, где для каждого будет пропускаться переход на следующий элемент в HastTable

0 голосов
/ 13 сентября 2009

Просто дополнение:

a для каждого ... в цикле не гарантирует, что элементы в массиве / векторе получат Перечисленные в ПОРЯДКЕ, ОНИ ХРАНЯТСЯ в них. (кроме XML) Это существенная разница, ИМО.

"... Следовательно, вы не должны писать код, который зависит от порядок перечисления цикла "каждый в" или "для", если вы не обрабатываете Данные XML ... "C.Moock

(я надеюсь не нарушать закон, утверждая эту одну фразу ...)

Счастливый бенчмаркинг.

0 голосов
/ 18 июня 2009

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

в результате этого я сделал подробный пост в своем супер новом блоге ...: D

Greetz

back2dos

...