Комментарии 61
В хроме показывает «14% slower».
AFAIK, принято считать, что цикл while эквивалентен for, во всяком случае в JavaScript. Наверное поэтому автор и не упомянул while.
Да, и у вас в коде ошибка, должно быть:
Да, и у вас в коде ошибка, должно быть:
var i = testData.length - 1;
Я когда то делал тесты ( уже не найду ), и там зависит от браузера и того что перебирать, на сколько я помню DOM быстрее перебирать с начала, да и разница там была минимальная и частенько такой вариант проигрывал, зависит от браузера.
Скорость перебора forEach тоже зависит от браузера ( тоже делал тесты ), в хроме forEach работает по скорости почти как for, но тоже зависит от данных.
П.с Нужно избегать использования delete в цикле, из за него скорость падает в разы
Скорость перебора forEach тоже зависит от браузера ( тоже делал тесты ), в хроме forEach работает по скорости почти как for, но тоже зависит от данных.
П.с Нужно избегать использования delete в цикле, из за него скорость падает в разы
В этой теме обсуждалось о скорости циклов ( ВНИМАНИЕ! В теме присутствует флуд и нецензурная лексика ) javascript.ru/forum/offtopic/43511-kak-najjti-razlichie-mezhdu-dvumya-massivami.html
Немного поздновато но все же.
jsperf.com/for-until-length-vs-until-undefined/18
В хроме быстрей for backwards в лисе while backwards.
jsperf.com/for-until-length-vs-until-undefined/18
В хроме быстрей for backwards в лисе while backwards.
Отмечу, что без return total хром вообще пропускает вычисления. (для наглядности jsperf.com/for-until-length-vs-until-undefined/20 )
С одной стороны тест, приведённый Вами показывает, что оптимизатор у фирефоха хуже всех пропускает блоки фиктивных вычислений.
С другой — показателем вычислений так же сложно считать и приведённый мною тест, т.к. анчар его знает, как они оптимизируют вызовы функций, выполняющие одно и то же (может смотрят, что возвращается одно и то же, что массив не меняется и значит можно сразу возвращать значения)
Реальные тесты это пипец, как не просто =) Лучше всего создать N массивов с рендомными значениями, дальше внутри теста при каждом следующем вызове брать следующий массив пока указатель не станет равен N. (и дальше сбросить его). Если массивов больше нескольких сотен, то хороший шанс, что оптимизатор не поймёт что не так. Результат вычислений так же следует куда-то складываь (например window.result += total в конце функции)
Скажу сразу: если на простых операциях (перебор, сложения/вычитания, математика и строки) вы видите большую дельту ФФ и хрома — значит где-то тест оптимизирован до безобразия (пропущен), что маловероятно на реальных данных.
С одной стороны тест, приведённый Вами показывает, что оптимизатор у фирефоха хуже всех пропускает блоки фиктивных вычислений.
С другой — показателем вычислений так же сложно считать и приведённый мною тест, т.к. анчар его знает, как они оптимизируют вызовы функций, выполняющие одно и то же (может смотрят, что возвращается одно и то же, что массив не меняется и значит можно сразу возвращать значения)
Реальные тесты это пипец, как не просто =) Лучше всего создать N массивов с рендомными значениями, дальше внутри теста при каждом следующем вызове брать следующий массив пока указатель не станет равен N. (и дальше сбросить его). Если массивов больше нескольких сотен, то хороший шанс, что оптимизатор не поймёт что не так. Результат вычислений так же следует куда-то складываь (например window.result += total в конце функции)
Скажу сразу: если на простых операциях (перебор, сложения/вычитания, математика и строки) вы видите большую дельту ФФ и хрома — значит где-то тест оптимизирован до безобразия (пропущен), что маловероятно на реальных данных.
Ну перед тем как комментить я себе написал тест под NodeJS примерно как вы описали.
Геренились массивы с рандомными данными. Итог был в пользу обратного for'a.
А в целом не особо критично главное что forEach зло. =)
Геренились массивы с рандомными данными. Итог был в пользу обратного for'a.
А в целом не особо критично главное что forEach зло. =)
Попробовал изголиться на реальных данных несколько приумноженных. iojs 1.5 while с зада оказался лучшим вариантом =)
Странно, разницы в коде быть не должно…
Т.е. что с конца идти в for что с начала во while по идее время должно отличаться менее чем на 5% + если тесты скорости запускать «зеброй» (не по тесту на секцию, а чередовать) время вообще отличаться не должно особо…
А код в студию можно?)
Поясню: for (i = 0; i < l; i++) и i=l; while (i >= 0) i--; превращаются фактически в одни и те же команды JIT, разве что минус и плюс разнятся.
А что forEach зло так это старо, как мир =) Кофескрипт в помощь ленивым так сказать =)
Т.е. что с конца идти в for что с начала во while по идее время должно отличаться менее чем на 5% + если тесты скорости запускать «зеброй» (не по тесту на секцию, а чередовать) время вообще отличаться не должно особо…
А код в студию можно?)
Поясню: for (i = 0; i < l; i++) и i=l; while (i >= 0) i--; превращаются фактически в одни и те же команды JIT, разве что минус и плюс разнятся.
А что forEach зло так это старо, как мир =) Кофескрипт в помощь ленивым так сказать =)
--i
А так будет еще немного быстрее.
Недавно столкнулся с тем, что привычный мне способ пробегать по массиву, используя map, reduce, filter, every, any и пр. (десятки их) методы, внутри генераторов принуждает меня либо отказаться от заветных yield-ов (потому что они работают только в теле генератора, а callback — метод), либо использовать встроенные в синтаксис языка конструкции. А учитывая что nodejs всё никак не хочет добавить for-of стало как-то грустно.
Поглядывая на io, я понимаю, почему они решили отколоться. А ещё там есть стрелочные функции… полагаю, что это очень удобная штука для функционального подхода.
Поглядывая на io, я понимаю, почему они решили отколоться. А ещё там есть стрелочные функции… полагаю, что это очень удобная штука для функционального подхода.
нет там стрелочных функций
Правда? :( Ориентировался на этот комментарий (--harmony-arrow-functions). Руки проверить пока не дошли.
да с флагом работают
Кстати под Windows инсталлятор сносит нафиг ноду. Не знаю как под другими ОС.
Так что если хочется поэксперементировать только, то надо качать в чистом виде, а не инсталлер.
Так что если хочется поэксперементировать только, то надо качать в чистом виде, а не инсталлер.
подтверждаю, правда я использую вот этот менеджер и оно конфликтит пока github.com/coreybutler/nvm-windows. Думаю написать автору по поводу поддержки io.js
var a = ["a", "b", "c"];
var entry;
while (!(entry = a.next()).done) {
console.log(entry.value);
}
Итератор тут явно не получают, а у массива нет метода
next
.Преобразование в настоящий массив
Про
for-of
с итераторами заикнулись, а про Array.from
ни слова.Не пойму, о чем вы пытаетесь спорить со мной (или с OP). О том, что ни у одного объекта пока нет метода next, или что массив сможет иметь итератор?
Пытаюсь спорить? :) Констатирую факт, что пример не верный в принципе. И стандартные возможности протокола итераторов сейчас поддерживаются везде, кроме IE, да и на нём легко реализуются полифилами.
Как я понял, метод
next
есть. Но не у массива, а у его итератора и вам нужно переписать код, чтобы он сначала получал итератор, а потом уже использовал next
, чтобы получить код, эквивалентный for of
. То, что есть, не является эквивалентным кодом.Поясните за этот регексп /^0$|^[1-9]\d*$/, он там для чего и почему бы не заменить его на ^\d+$ или даже что-то такое «Number(key) === key && key%1 === 0»
Там же — пишете что «key — строка» и проверяете как «key <= 4294967294»
Там же — пишете что «key — строка» и проверяете как «key <= 4294967294»
Поясните за этот регексп /^0$|^[1-9]\d*$/
Это:
строка, содержащая десятичную запись целого числа, значение которого меньше 4294967294
^\d+$
допускает недесятичные числа как напр-р: 012.Там же — пишете что «key — строка» и проверяете как «key <= 4294967294»
При сравнении с числом строка всегда приводится к числу.
Вообще, как указано в статье, это формальная (я бы даже сказал занудная) проверка. В большинстве случаев можно использ-ть более простую как:
String(parseInt(key, 10)) === key
У меня в firefox forEach медленнее в ≈30 раз, в Chromium — >60 раз. Подозреваю, что если forEach будут активно использовать, то браузеры начнут агрессивно встраивать код на основании предположений о типе this, а до тех пор придётся терпеть.
Думаю, что этот момент можно несколько приблизить, написав популярный(!) benchmark, проверяющий скорость выполнения ES6‐конструкций.
Думаю, что этот момент можно несколько приблизить, написав популярный(!) benchmark, проверяющий скорость выполнения ES6‐конструкций.
в javascript играет роль .length в условии цикла или нет? просто в php лучше присваивать переменной count перед циклом, но здесь в примерах везде идет в условии…
Насколько понимаю, в современных движках — нет.
Я не сказал, что он будет работать быстрее. Я скорее сказал: это иллюзия, что он будет работать медленнее «потому что
length
в цикле больше никто не читает» — как CPU положит, так и будет работать :) У меня есть машина (с Xeonом) там у меня получилось быстрее — я это исключительно для того в пост добавил, чтобы показать, что это все замеры погоды в северном полушарии.Если перебор коллекции, а не массива, то очень желательно сохранить длину, а по хорошему сохраняются всегда чтобы не пропустить, просто best practices, но сейчас возможно это уже и не так актуально.
Ну и ряд функциональных подходов
- Tail optimized recursion
function loop(fn) { var index = 0; return function over(array) { if (index >= array.length) { return; } fn(array[index], index, array); index++; over(array); }; } var consoleLog = loop(function (item, index, array) { console.log(item, index, array); }); consoleLog([1, 2, 3, 4]);
jsbin.com/rimabu/3/edit?js
- Y Combinator
function Y(le) { return function(f) { return f(f); }(function(f) { return le(function(x) { return (f(f))(x); }); }); } function loop(fn) { var index = index || 0; return function (over) { return function (array) { if (index >= array.length) { return; } fn(array[index], index, array); index++; over(array); }; }; } var consoleLog = Y(loop(function (item, index, array) { console.log(item, index, array); })); consoleLog([1, 2, 3, 4]);
jsbin.com/ligawa/2/edit?js
Для Chrome и FF пока самый практичный вариант это прямой перебор с предварительным вычислением длины массива.
jsperf.com/function-call-cost-on-ie6/9
При этом для FF кэш длины массива не так важен.
jsperf.com/function-call-cost-on-ie6/9
При этом для FF кэш длины массива не так важен.
Для преобразования можно использовать универсальный метод Array.prototype.slice, который может быть применен к любому массивоподобному объекту.
Или Array.from() из ECMAScript 6, что читается яснее.
Через setTimeout еще можно перебрать (в этом случае как правило в массиве функции), допустим для тяжелых циклов чтобы браузер не выдавал окна о долгом выполнении скриптов, неблокирующее/асинхронное выполнение ряда функций.
Нормальная статья. Стоит упомянуть, что делать Array.prototype.slice.call(arguments) не рекомендуют.
На мой взгляд не был упомянут вот такой очень удобный и универсальный подход:
var items = [1, 2, 3];
[].forEach.call(items, function(item, index) {
console.log(item, index);
});
1. forEach упомянут.
2. Зачем создавать ещё один массив, и брать из него forEach, если он итак есть в items? o_O
2. Зачем создавать ещё один массив, и брать из него forEach, если он итак есть в items? o_O
Имелось ввиду, что items — не массив.
Разреженные массивы — очень плохая практика: V8 и SpiderMonkey очень много памяти съедают, при чём отследить не просто: 10 элементов на всём диапазоне может норм, а 100 уже хоп(!) и +200Мб. (ВМ-е JIT-а выдаётся не так много памяти, а если включить мобильные версии в кандидаты исполнения, то...)
Вывод: как правило, если нужен
Вывод: как правило, если нужен
for ... in
для массива, значит что-то идёт не так)Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Все способы перебора массива в JavaScript