Почему производительность веб-работника резко снижается через 30 секунд?
Я пытаюсь улучшить производительность script при выполнении веб-работника. Он предназначен для синтаксического анализа больших текстовых файлов в браузере без сбоев. Все работает очень хорошо, но я замечаю серьезную разницу в производительности для больших файлов при использовании веб-рабочего.
Итак, я провел простой эксперимент. Я дважды запускал script на том же входе. Первый запуск выполнял script в основном потоке страницы (нет веб-работников). Естественно, это заставляет страницу замораживаться и становиться невосприимчивой. Для второго запуска я выполнил script в веб-работнике.
Для небольших файлов в этом эксперименте (< 100 МБ) разница в производительности незначительна. Однако в больших файлах синтаксический анализ занимает около 20 раз дольше в рабочем потоке:
![Performance of both scenarios on same graph]()
Ожидается синяя линия. Для анализа файла требуется всего около 11 секунд, а производительность довольно устойчивая:
![Performance of script without web worker]()
Красная линия - это производительность внутри веб-рабочего. Это гораздо более удивительно:
![Performance of script in web worker]()
Неисследованная линия в течение первых 30 секунд является нормальной (зависание вызвано небольшой задержкой при отправке результатов в основной поток после разбора каждого фрагмента файла). Однако синтаксический анализ замедляется довольно резко на 30 секунд. (Обратите внимание, что я использую только один веб-рабочий для работы, не более одного рабочего потока за раз.)
Я подтвердил, что задержка не заключается в отправке результатов в основной поток с помощью postMessage()
. Замедление находится в узкой петле синтаксического анализатора, которая полностью синхронна. По причинам, которые я не могу объяснить, этот цикл резко замедляется, и он становится медленнее со временем через 30 секунд.
Но это происходит только в веб-работнике. Выполнение того же кода в основном потоке, как вы видели выше, выполняется очень плавно и быстро.
Почему это происходит? Что я могу сделать для повышения производительности? (Я не ожидаю, что кто-нибудь сможет полностью понять все 1200 строк кода в этом файле. Если вы это сделаете, это потрясающе, но я чувствую, что это больше связано с веб-работниками, чем с моим кодом, поскольку он отлично работает в основном нить.)
Система: я запускаю Chrome 35 на Mac OS 10.9.4 с 16-гигабайтной памятью; четырехъядерный процессор Intel Core i7 с тактовой частотой 2,7 ГГц с кешем L2 объемом 256 КБ (на ядро) и кеш-память L3 объемом 6 МБ. Размер фрагментов файлов составляет около 10 МБ.
Обновление: Просто попробовал его в Firefox 30, и он не испытывал такого же замедление в рабочем потоке (но он был медленнее, чем Chrome, когда он запускался в основном потоке). Тем не менее, попытка того же эксперимента с еще большим файлом (около 1 ГБ) привела к значительному замедлению примерно через 35-40 секунд (кажется).
Ответы
Ответ 1
Tyler Ault предложил одну возможность в Google+, которая оказалась очень полезной.
Он предположил, что использование FileReaderSync
в рабочем потоке (вместо простого аланинга FileReader
) не дает возможности для сбора мусора.
Изменение рабочего потока для использования FileReader
асинхронно (что интуитивно похоже на шаг производительности назад) ускорило процесс вплоть до 37 секунд, прямо там, где я ожидаю.
Я еще не слышал от Tyler, и я не совсем уверен, что понимаю, почему сборщик мусора будет виновником, но что-то о FileReaderSync
резко тормозило код.
Ответ 2
На каком оборудовании вы работаете? Возможно, вы столкнулись с проблемами с кешем с вашим процессором. Например, если кэш ЦП составляет 1 МБ на ядро (только пример), и вы начинаете пытаться работать с данными, постоянно заменяющими кеш (промахи в кеше), тогда вы будете испытывать замедление - это довольно часто встречается в системах MT. Это также характерно для переносов ввода-вывода. Кроме того, эти системы, как правило, имеют некоторые накладные расходы ОС для контекстов потоков. Таким образом, если много нитей порождаются, вы можете тратить больше времени на управление контекстами, чем поток - "выполнение работы". Я еще не посмотрел на ваш код, поэтому я мог бы уйти, но я думаю, что проблема связана с памятью только из-за того, что делает ваше приложение.:)
О. Как исправить. Попробуйте сделать блоки исполнения маленькими одиночными кусками, которые соответствуют аппаратным средствам. Минимизируйте количество потоков в использовании сразу - постарайтесь удержать их в 2-3 раза от количества ядер, которые у вас есть на аппаратном обеспечении (это действительно зависит от того, какой у вас есть). Надеюсь, что это поможет.