Что произойдет, если я скажу вам, что все, что вы знали, было ложью, если вы изучите некоторые из основных функций столь любимого ECMAScript, выпущенного в последние годы, которые могут легко привести к проблемам с производительностью.
Действие происходит несколько лет назад, так что вернемся в наивные времена ES5...
Я с любовью вспоминаю тот день, когда был выпущен ES5, наш любимый Javascript представил несколько замечательных методов работы с массивами, forEach, reduce, map, filter — методы, которые заставляли нас чувствовать, что язык развивается и становится все более и более мощным, написание кода становится более интересным и жидкость, и код становится более доступным.
Почти в то же время родился Node.js, который позволил нам плавно перейти от внешнего интерфейса к серверному, по-настоящему переосмыслив разработку полного стека.
Теперь Node.js, использующий новейший ECMAScript на движке V8, стремится считаться одним из основных серверных языков разработки. Следовательно, он должен доказать свою эффективность с точки зрения производительности. Конечно, необходимо учитывать множество параметров производительности, и ни один язык не может работать лучше, чем любой другой, по всем параметрам. Однако является ли написание javascript готовыми методами, такими как упомянутые выше функции, полезным или вредным для производительности вашего приложения?
Кроме того, javascript считается разумным решением для разработки на стороне клиента не только для отображения представлений, потому что производительность компьютера пользователя станет лучше, а сеть будет быстрее, но когда нам нужно сверхвысокопроизводительное приложение или очень сложное. Можем ли мы положиться на компьютере пользователя для приложения?
Чтобы проверить эти проблемы, я пытаюсь сравнить несколько сценариев и получить представление о результатах своих экспериментов, которые я проводил в Node.js v10.11.0, браузере Chrome, macOS.
1. Обходим массив
Первый сценарий, который я сделал, заключался в суммировании массива из 100 000 элементов данных. На самом деле это правильный подход, я получаю список из базы данных и суммирую его, никаких дополнительных операций с БД.
Я использовал for , for-of, while, forEach, reduce для сравнения суммирования случайных 100 000 фрагментов данных, и вот результаты:
For Loop, average loop time: ~10 microseconds
For-Of, average loop time: ~110 microseconds
ForEach, average loop time: ~77 microseconds
While, average loop time: ~11 microseconds
Reduce, average loop time: ~113 microseconds
При поиске в Google, как выполнить суммирование массивов, наилучшей рекомендуемой реализацией является сокращение, но худшая производительность. Мой обязательный метод forEach также работает не очень хорошо. Даже последний метод ES6, for-of , просто обеспечивает наихудший метод. Это в 10 раз хуже, чем старый метод цикла for (также самый эффективный метод).
Как последний и наиболее рекомендуемый метод может сделать Javascript настолько медленным, на это есть две основные причины. reduce и forEach требуют выполнения callback-функции, которая вызывается рекурсивно и «раздувает» стек, а также дополнительных операций и проверки исполняемого кода.
2. Скопируйте массив
Копирование массива не кажется интересным сценарием, но это краеугольный камень неизменяемых функций, которые не изменяют ввод при создании вывода.
Тест производительности тоже показал интересные результаты — при копировании 100 000 случайных данных старый метод все равно оказался быстрее нового. Используя расширенные операции ES6 [...arr] и Array.from, а также метод карты ES5, производительность arr.map(x=>x) не так хороша, как у старого метода arr.slice() и метода конкатенации [].concat (обр.)
Duplicate using Slice, average: ~367 microseconds
Duplicate using Map, average: ~469 microseconds
Duplicate using Spread, average: ~512 microseconds
Duplicate using Conct, average: ~366 microseconds
Duplicate using Array From, average: ~1,436 microseconds
Duplicate manually, average: ~412 microseconds
3. Итерация объекта
Другим часто встречающимся сценарием является итерация объекта, обычно мы не можем получить значение в соответствии с определенным ключом, но должны пройти через структуру JSON или объект. У нас есть старый метод for-in (for(let key in obj)) и новые методы Object.keys(obj) и Object.entries(obj).
Мы использовали описанный выше метод для профилирования 100 000 объектов, каждый из которых содержит 1000 случайных ключей и значений. Результат выглядит следующим образом:
Object iterate For-In, average: ~240 microseconds
Object iterate Keys For Each, average: ~294 microseconds
Object iterate Entries For-Of, average: ~535 microseconds
Причина такого результата в том, что последние две схемы создают перечислимый массив значений вместо обхода массива напрямую без ключей. Но на конечный результат все равно есть на что посмотреть.
В заключение
Мой вывод очевиден — если производительность критична для вашего приложения, или ваш сервис должен справляться с некоторой перегрузкой, то использование крутых, более читаемых и чистых методов может оказать значительное влияние на производительность вашего приложения — может быть, в 10 раз медленнее!
Впоследствии, прежде чем слепо следовать новым тенденциям, убедитесь, что эти новые методы соответствуют вашим потребностям.Для небольших приложений быстрая итерация и высокая читаемость кода идеально подходят для кода, но для загруженных серверов и больших клиентских приложений это может быть не лучшей практикой.