Подтвердить что ты не робот

Почему производительность веб-работника резко снижается через 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 секунд (кажется).

4b9b3361

Ответ 1

Tyler Ault предложил одну возможность в Google+, которая оказалась очень полезной.

Он предположил, что использование FileReaderSync в рабочем потоке (вместо простого аланинга FileReader) не дает возможности для сбора мусора.

Изменение рабочего потока для использования FileReader асинхронно (что интуитивно похоже на шаг производительности назад) ускорило процесс вплоть до 37 секунд, прямо там, где я ожидаю.

Я еще не слышал от Tyler, и я не совсем уверен, что понимаю, почему сборщик мусора будет виновником, но что-то о FileReaderSync резко тормозило код.

Ответ 2

На каком оборудовании вы работаете? Возможно, вы столкнулись с проблемами с кешем с вашим процессором. Например, если кэш ЦП составляет 1 МБ на ядро ​​(только пример), и вы начинаете пытаться работать с данными, постоянно заменяющими кеш (промахи в кеше), тогда вы будете испытывать замедление - это довольно часто встречается в системах MT. Это также характерно для переносов ввода-вывода. Кроме того, эти системы, как правило, имеют некоторые накладные расходы ОС для контекстов потоков. Таким образом, если много нитей порождаются, вы можете тратить больше времени на управление контекстами, чем поток - "выполнение работы". Я еще не посмотрел на ваш код, поэтому я мог бы уйти, но я думаю, что проблема связана с памятью только из-за того, что делает ваше приложение.:)

О. Как исправить. Попробуйте сделать блоки исполнения маленькими одиночными кусками, которые соответствуют аппаратным средствам. Минимизируйте количество потоков в использовании сразу - постарайтесь удержать их в 2-3 раза от количества ядер, которые у вас есть на аппаратном обеспечении (это действительно зависит от того, какой у вас есть). Надеюсь, что это поможет.