Для работы VS Foreach on Array (в AS3/Flex)

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

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) {
    // ...
}

Ответы

Ответ 1

Откуда я сижу, регулярные циклы 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: Ответы на комментарий oshyshko:

  • Компилятор может пропускать обращения в моих внутренних циклах, но это не так. Петли выходили бы в два-три раза быстрее, если бы это было.
  • Результаты, полученные в примере кода, который вы опубликовали, поскольку в этой версии цикл for теперь имеет неявное преобразование типа. Я оставил задания из своих петель, чтобы избежать этого. Разумеется, можно утверждать, что в цикле for может быть добавлено дополнительное выделение, потому что "реальный код" в любом случае нуждается в этом, но для меня это просто еще один способ сказать "нет никакого общего ответа: какой цикл быстрее зависит от что вы делаете внутри своей петли". Какой ответ я вам даю.;)

Ответ 2

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

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 относительно дорог, а доступ к массиву также относительно медленный. С a для каждого цикла обе операции обрабатываются изначально, я думаю, как показано в ActionScript.

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

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

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

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

forEachLoop: 328 whileLoop: 366

forEachLoop: 324 whileLoop: 369

Ответ 3

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

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);
}

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

Примечания:

-Приведение массива в локальную переменную в функции цикла "для каждого" не имеет значения, но в цикле "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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

Просто добавление:

a для каждого цикла... в не гарантирует, что элементы в массиве array/vector будут перечислены в ЗАКАЗЕ, ОНИ ЗАПРЕЩЕНЫ в них. (кроме XML) Это существенная разница, ИМО.

"... Поэтому вы не должны писать код, который зависит от for- порядок перечисления в каждом или в конце цикла, если вы не обрабатываете XML-данные... "C.Moock

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

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

Ответ 8

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

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

Greetz

back2dos