Анализ производительности и предвзятости циклов и методов JavaScript при работе с различными наборами данных.

Введение в циклы и методы
Мы собираемся взглянуть на различные циклы и методы, предоставляемые в JavaScript, чтобы выяснить, какие из них более эффективны для данных, которые вы повторяете. Причина, по которой я потратил время на то, чтобы собрать это воедино, кроется в наблюдении за развитием разработчиков, а также за тем, как они используют и формируют мнения о различных циклах и методах.
Все начинают с вашего основного цикла for. Как только новые разработчики узнают об этом, их умы взрываются, и жизнь становится проще. Этот умопомрачительный опыт происходит снова и снова по мере появления новых методов. Что интересно, как только вводятся новые цикл и методы (while, forEach, map, filter и т. Д.), Основной цикл for надолго остается в тени. Это происходит где-то с первой по третий месяц и далее. Разработчику требуется много времени или определенный опыт работы с данными, чтобы вернуться и снова рассмотреть базовый цикл for для достижения своих целей.
По этой причине мы собираемся увидеть, есть ли какое-либо оправдание для использования только таких методов, как forEach и map, или есть ли какие-либо преимущества в том, чтобы придерживаться испытанного и истинного цикла for.
Типы данных
Мы собираемся рассмотреть каждый из этих циклов, чтобы выявить их преимущества и недостатки по сравнению с примитивными и непримитивными типами данных. Если вам нужно напомнить об этих типах данных, вот список, с которым вы обычно работаете.
Примитивы
- Цифры
- Струны
- Логические
- "Неопределенный"
- "Нулевой"
Непримитивный
Поиск значения в массиве
Наши тесты покажут нам, насколько эффективны наши циклы при извлечении в зависимости от возвращаемого значения, значения или индекса значения из массива. Мы будем использовать следующие циклы:
- "за"
- За… из
- "для каждого"
- "пока"
- "делать пока"
- "найти"
- FindIndex
- "индекс чего-либо"
- LastIndexOf
- "включает"
- "карта"
- "фильтр"
- "уменьшать"
Давайте начнем с небольшого примера, который отображает каждый из этих циклов поиска примитивного значения a из массива образцов. Обратите внимание, что мы будем немного более подробными, чем некоторые из «однострочных» функций, чтобы захватить больше значений.
Примеры примитивных массивов:
let namesArray = ['Abe', 'Beth', 'Cody', 'Daniel']; let textArray = ['Dog', 'Cat', 'Horse', 'Cow']; let numbersArray = [1, 2, 3, 4];
Стартовый код
// Objectives: // 1. Find the value 7 // 2. Find the index of 7 const OBJECTIVE_NUMBER = 7; let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let foundValue; let foundIndex = -1;
Приведем пример кода, который мы будем использовать для сравнительного анализа. Чтобы увидеть полный список примеров циклов и методов, нажмите здесь!
Пример "цикла"
// Using array and variables from base code block above…
for (let index = 0; index < arr.length; index++) {
const value = arr[index];
if(value === OBJECTIVE_NUMBER) {
foundValue = value;
foundIndex = index;
break;
}
};
console.log(foundValue); // expected output: 7;
console.log(foundIndex); // expected output: 6;
Сравнительный анализ кода
Теперь, когда у нас есть базовое представление о каждом из циклов и возможностях, которые они привносят в таблицу, мы можем увидеть, как они работают с небольшими и большими наборами данных. Мы собираемся включить map, filter и reduce, даже если они используются как анти-шаблон, чтобы продемонстрировать производительность по всем направлениям. Мы также протестируем наши итерации на поиск значения около начала и конца массива для каждого цикла и метода. Мы также протестируем их в разных браузерах, чтобы измерить производительность движков JavaScript каждого браузера (Chakra, V8 и SpiderMonkey), которые повторяют и оптимизируют наши циклы в фоновом режиме.
Массивы, которые мы будем использовать:
- Массив 1: 100 примитивных значений;
- Массив 2: 1000 примитивных значений;
- Массив 3: 10 000 примитивных значений;
Примечание. В части 2 мы рассмотрим те же циклы, но в сравнении с непримитивами (объектами, массивами, функциями) и измерим производительность по ним.
Окончательные результаты
Прежде чем мы поговорим о следующих результатах, помните, что производительность будет варьироваться в зависимости от оборудования и программного обеспечения для каждого пользователя. В результате мы, как разработчики, должны планировать наихудшие сценарии, чтобы обеспечить оптимальную работу пользователей на всех платформах и устройствах. Имея это в виду, давайте посмотрим, как выполнялись наши циклы при поиске примитивного значения внутри массива.
Примечание. На графиках представлен каждый цикл или метод, а также количество операций в секунду (операций в секунду), выполняемых в течение заданного периода времени.
Хром

Край

Fire Fox

Разбивка результатов
Посмотрев на графики, можно сделать несколько общих выводов:
- По мере увеличения наборов данных map, reduce и filter работают хуже всего, если используются против их предполагаемого назначения или определения.
- В отличие от небольших массивов движок Firefox (SpiderMonkey) оптимизирован для всех методов, чтобы перебирать массивы и находить значения как в начале, так и в конце указанных массивов.
- lastIndexOf работает нормально. Хуже при поиске в начале массива и лучше всего при поиске конечных значений. Поскольку это ожидается, мы удалим этот метод при сравнении общей производительности.
Массивы малых размеров
Давайте начнем с небольших массивов для некоторых общих выводов.
- Edge: forEach, map и reduce работают лучше всего.
- Chrome: forEach, map и reduce работают лучше всего.
- Firefox: все методы, кроме map, filter и reduce, работают хорошо, но не намного.
- Общий результат: forEach
Массивы среднего размера
Далее мы замечаем, что с массивами среднего размера, особенно при поиске значений в конце массива, производительность начинает сильно меняться по всем циклам и методам.
- Edge: indexOf и includes работают лучше, за ними следует while, do… while, for и for… of.
- Chrome: indexOf и includes принимают пирог за производительность, за которыми следуют for, while и do… while.
- Firefox: здесь зафиксирована более высокая производительность, чем в Edge и Chrome. for, while, indexOf и includes - все высокопроизводительные.
- Показатели в целом: indexOf and while, поскольку мы обычно смотрим от корки до корки в поисках нашей ценности.
Массивы большого размера
Наконец, мы видим гораздо большее падение производительности по мере увеличения размера нашего массива.
- Edge: for, while и indexOf работают лучше всего. Большинство других циклов и методов работают плохо.
- Chrome: пока indexOf и includes остаются наверху, хотя мы снова видим, что большинство других методов не работают на том же уровне.
- Firefox: for, while и indexOf снова являются главными претендентами с таким же падением, наблюдаемым с большинством оставшихся циклов и методов.
- Показатели общего успеха: пока и пока.
Вывод
Надеюсь, что в результате просмотра данных мы все сможем принимать более правильные решения о методах, которые мы хотим использовать для различных наборов данных. Если мы работаем с данными, которые со временем могут расти, и нам приходится перебирать все эти данные, может быть целесообразно вернуться к надежному циклу for, который всегда был рядом с нами. Тем более, что вы можете воспользоваться его наследуемой способностью останавливаться до цикла с помощью break и return, как только вы закончите намеченное действие. Хотя это может выглядеть не очень красиво, оно всегда будет удобно.

Во второй части мы покажем аналогичные данные, но познакомим вас с поиском не примитивов, чтобы увидеть, как меняются результаты. Это должно быть еще более актуально для повседневных задач, поскольку большая часть данных, с которыми мы работаем, возвращается в виде массивов JSON, заполненных объектами из базы данных.
Обновление: Часть 2 доступна здесь.