Во-первых, нет ничего плохого в том, чтобы сделать это как УЛУЧШЕНИЕ существующей доставки на стороне сервера или если это серверный JS. ИЛИ, если это внутренний сценарий апплета, не предназначенный для публичного просмотра.
Это плохая идея только на веб-сайтах, и даже тогда ЕСТЬ исключения (например, игры только со сценарием). Эта клиентская нагрузка БУДЕТ отстойной, если ее разместить на обычном веб-сайте, поэтому я бы не стал этого делать там, но ЕСТЬ места, где этот подход действителен.
Даже я не такой уж и придурок в этом вопросе.
Похоже, у вас есть какая-то библиотека, воспроизводящая СУЩЕСТВУЮЩУЮ ФУНКЦИОНАЛЬНОСТЬ.
ох, это чертовски круто.
Лишние вызовы функций могут увеличить время выполнения и накладные расходы, поэтому откажитесь от них.
Например, _.lowerCase, который, как я полагаю, является просто оболочкой для String.toLowerCase? То же самое и с _.filter.
Я предполагаю, что это массивы, почему бы не использовать NATIVE Array.filter? (это не значит, что если вам НУЖНА устаревшая поддержка, вы не можете напрямую заполнить массив) - это часть того, почему я не люблю lodash.
Он копирует дерьмо, которое не нужно было копировать! У вас получился отличный массив, используйте обычный повседневный Array.filter!!!
То, что они безумно называют обратный вызов «предикатом», — это просто еще один тупой язык, из-за которого я всегда жалуюсь.
Далее, в БОЛЬШИНСТВЕ браузеров (квантово, это стирка) использование анонимных функций медленнее, чем статических функций, поэтому, если вам не нужна привязка области, вы можете добиться большей скорости, не используя анонимные функции в качестве обратных вызовов.
Можно было бы также увеличить скорость, если бы вы мутировали параметры вместо того, чтобы создавать новые переменные.
Например, «запрос», где вы никогда не используете его после переопределения регистра, так почему бы вместо этого просто не ввести его напрямую (toLowerCase)?
Кроме того, пропуск точек с запятой заставляет синтаксический анализатор работать усерднее.
и, как ни странно, сохранение длины в собственной переменной на самом деле НЕ дает вам повышения производительности.
(когда/где это дает вам импульс, это часто противоречит здравому смыслу, подъем — это странно)
Да, и ваш тестовый цикл зацикливает только ваше ПЕРВОЕ действие, вы, вероятно, хотели зациклить ВСЕ из них.
Кроме того, время, необходимое для запуска теста, настолько мало, что оно ниже «коэффициента дрожания» для детализации таймера Performance.now(). Вот почему я предпочитаю выполнять цикл до тех пор, пока таймер не перевернется, а затем выполнить цикл, чтобы увидеть, сколько я могу выполнить за определенный период времени.
Итак, что-то вроде:
Original Rewrite Improvement
Celeron J1900, 8 gigs RAM
FF Quantum 61 585 9.59x
Vivaldi (chromium) 118 1047 8.87x
Edge 46 523 11.36x
IE11 48 476 9.91x
i7 4770k, 24 gigs RAM
FF Quantum 148 1145 7.73x
Vivaldi (chromium) 310 2322 7.49x
Edge 121 990 8.18x
IE11 123 1149 9.31x
Код (разметка): Попытка использовать фиксированное одиночное прохождение или даже количество прогонов, а затем учитывать затраченное время, может быть весьма ошибочной.
У вас НАМНОГО больше шансов получить правильные результаты, ожидая обновления таймера, а затем работая в течение фиксированного периода времени.
Также позволяет настраивать большие тесты, не беспокоясь о тайм-ауте браузера.
(Ошибка №1, которую я видел в тестах JS!)
Дайте-ка посмотреть.
ах, JSBins портит автозаполнение и бесполезно маленькие редакторские коробки борются со мной, так что скопируйте на локальный компьютер и настройте там.
Хм. Вы знаете, во всяком случае, клавиши карты, вероятно, ДЕЙСТВИТЕЛЬНО занимают здесь больше всего времени! Возникнет соблазн как-нибудь отложить это отдельно.
Ага.
ок, что ДЕЙСТВИТЕЛЬНО является вашим узким местом, так это постоянное переключение между массивом и объектом, массивом и объектом с использованием лоадэша "о, мы будем обращаться со всеми объектами как с массивами настолько неэффективно, насколько это возможно".
ПЕРВОЕ, что я бы посоветовал: если вы хотите, чтобы объект индексировался по идентификатору, СОХРАНИТЕ ИХ КАК ИДЕНТИФИКАТОРЫ!!!
А пока давайте поработаем с тем, что у вас есть, и вышвырнем толстую задницу на обочину.
Итак, сначала мы сделаем небольшую процедуру для преобразования вашего массива в объект:
function filterArticles(search, status) { search = search.toLowerCase(); var statusCallbacks = [ function(article) { return search.length ? article.title.toLowerCase().indexOf(search) > -1 : true; }, function(article) { return ( search.length ? article.title.toLowerCase().indexOf(search) > -1 : true ) && article.published; }, function(article) { return ( search.length ? article.title.toLowerCase().indexOf(search) > -1 : true ) && !article.published; } ], useCallback = statusCallbacks[statusCallbacks[status] ? status : 0], results = {}; for (var i in articles) if (useCallback(articles[i])) results[i] = articles[i]; return results; }
Код (разметка): Тогда давайте перепишем вашу процедуру, чтобы ВСЕГДА работать с ней как с объектом, вместо тупого преобразования Loadass в массив обратно в объект в мусорный массив.
var articles = {}; for (var i = 0, e; e = articlesArray[i]; i++) articles[e.id] = e;
Код (разметка): Да, вот и все.
Просто работайте над новым объектом непосредственно во время создания.
Ваш оригинал дал мне в среднем 320 проходов, этот перезапись — 2210, то есть фактически в семь раз быстрее.
Возможно, стоит рассмотреть и другие варианты оптимизации, например, полный отказ от использования обратных вызовов — новейшую версию Spidermonkey от Quantum, вероятно, это не волнует, но это может оказать влияние на Chakra и V8. Черт его знает, что сделают эти дерьмо, такие как Трайдент и Нитро.
Несколько быстрых тестов, в среднем более десяти тестов с 5-секундным временем восстановления, чем больше, тем лучше.
var start = performance.now(), iterations = 0; do { stop = performance.now(); } while (start == stop); stop += 1000; // time to run do { filterArticles('a', 1); filterArticles('b', 2); filterArticles('th', 2); filterArticles('dog', 1); filterArticles('a'); filterArticles('b'); filterArticles('th'); filterArticles('dog'); iterations++; } while (performance.now() < stop); console.log(iterations)
Код (разметка): цифры IE11 меня удивили, но движок JavaScript в IE не является настоящим узким местом — причина, по которой JavaScript IE всегда работал так медленно, заключается в том, насколько болезненно неэффективны DOM, синтаксический анализатор и средство рендеринга.
(ОСОБЕННО проклятый парсер, почему InnerHTML был таким мусором) особенно учитывая, что даже в процессе рендеринга НЕТ многопоточности.
Поскольку это даже не касается DOM или живого документа, мы можем видеть производительность JS без какого-либо вмешательства.
Дальнейшие улучшения, такие как отказ от использования обратных вызовов путем развертывания цикла, МОГУТ обеспечить ускорение в некоторых движках.
Нет, я только что попробовал, это не дает реального измеримого улучшения, в пределах ожидаемого «дрожания». Хм... объект? Нет, производительность снижается до уровня, используемого loadass.
ну, не так уж плохо, все равно примерно в два раза быстрее, чем ваш оригинал, но зачем соглашаться на 2x, если вы можете достичь 11x?
ЭТОТ Вот почему я говорю, что многие фреймворки JavaScript не заставляют вас «правильно думать» о решении проблем.
Они даже не УЧИТЫВАЮТ влияние на производительность постоянного переключения между массивом и объектом! «Ошибочная методология» простого использования lodash для этого СНОВА показывает то, что я всегда говорю об этих фреймворках — это медленнее, сложнее и БОЛЬШЕ кода, чем если бы вы делали это без фреймворка! Видите, как переписанная функция занимает всего 741 байт, тогда как оригинал составлял 1052 байта? Когда я говорю «меньше, быстрее, проще и легче БЕЗ тупых фреймворков», я
обычно знаю, о чем я говорю.
Я бросил здесь живые примеры:
http://www.cutcodedown.com/for_others/KewL/filter/
Надеюсь это поможет.