Распараллеливание: что заставляет потоки Java блокировать, кроме синхронизации и ввода / вывода? - PullRequest
7 голосов
/ 02 декабря 2008

Короткая версия есть в названии.

Длинная версия: Я работаю над программой для научной оптимизации с использованием Java. Рабочая нагрузка программы может быть разделена на параллельную и последовательную фазы - параллельные фазы, что означает, что выполняется высокопараллельная работа. Чтобы ускорить программу (она работает часами / днями), я создаю количество потоков, равное количеству ядер ЦП на машине, которую я использую - обычно 4 или 8 - и делю работу между ними. Затем я запускаю эти потоки и присоединяюсь к ним, прежде чем перейти к последовательной фазе.

Пока все хорошо. Что меня беспокоит, так это то, что загрузка ЦП и ускорение параллельных фаз не приближаются к «теоретическому максимуму» - например, если у меня 4 ядра, я ожидаю увидеть «загрузку» где-то между 350-400% (как сообщается сверху), но вместо этого он колеблется между 180 и 310. Используя только один поток, я получаю 100% загрузку ЦП.

Единственные известные мне причины, по которым потоки не работают на полной скорости: блокировка из-за ввода / вывода -блокировка из-за синхронизации

Никаких операций ввода-вывода в моих параллельных потоках и синхронизации не происходит - единственные структуры данных, общие для потоков, доступны только для чтения и являются либо базовыми типами, либо (не параллельными) коллекциями. Поэтому я ищу другие объяснения. Возможно, что несколько потоков неоднократно блокируются для сбора мусора, но это может иметь смысл только в ситуации с нехваткой памяти, и я выделяю намного больше требуемого максимального пространства кучи.

Любые предложения будут оценены.

Обновление: На всякий случай, если кому-то интересно, после еще одного исследования я настроил код для общей производительности и вижу лучшее использование, даже если я ничего не изменил в связи с синхронизацией. Однако некоторые изменения должны были привести к меньшему количеству новых выделений кучи, в частности, я избавился от некоторого использования итераторов и временных коробочных чисел (библиотека CERN "Colt" для производительность Java-вычислений была здесь полезна: она предоставляет коллекции, такие как IntArrayList, DoubleArrayList и т. д. для базовых типов.) Поэтому я думаю, что сборщик мусора был, вероятно, виновником.

Ответы [ 7 ]

5 голосов
/ 02 декабря 2008

Все графические операции выполняются на одном потоке в разгаре. Если они рендерится на экран, они фактически будут бороться за доступ к этой теме.

Если вы работаете в Windows, все графические операции выполняются в одном потоке, несмотря ни на что. Другие операционные системы имеют аналогичные ограничения.

На самом деле иногда довольно трудно получить правильную гранулярность потоковых рабочих, а иногда легко сделать их слишком большими или слишком маленькими, что обычно дает вам менее 100% использования всех ядер.

Если вы не используете много графического интерфейса, наиболее вероятным виновником является то, что вы конкурируете больше, чем вы думаете, за какой-то общий ресурс. Это легко увидеть с помощью инструментов профилирования, таких как jprofiler. Некоторые виртуальные машины, такие как jrockit от bea, могут даже сказать вам об этом прямо из коробки.

Это одно из тех мест, где вы не хотите действовать в догадках. Получить профилировщик!

4 голосов
/ 02 декабря 2008

Прежде всего, GC будет происходить не только «в ситуации с нехваткой памяти», но в любое время JVM сочтет нужным (насколько я знаю, непредсказуемым).

Во-вторых, если ваши потоки выделяют память в куче (вы упоминаете, что они используют коллекции, поэтому я предполагаю, что они выделяют память в куче), вы никогда не сможете быть уверены, находится ли эта память в оперативной памяти или на странице виртуальной памяти ( ОС решает), и, таким образом, доступ к «памяти» может генерировать блокировку доступа ввода-вывода!

Наконец, как предложено в предыдущем ответе, может оказаться полезным проверить, что происходит, с помощью профилировщика (или даже мониторинг JMX может дать некоторые подсказки).

Полагаю, вам будет сложно получить дополнительные подсказки по вашей проблеме, если вы не предоставите более конкретную (кодовую) информацию.

2 голосов
/ 02 декабря 2008

Во-первых, я предполагаю, что вы не выполняете никакой другой важной работы на коробке. Если да, то это явно будет мешать.

Звучит очень странно, если вы действительно ничего не делитесь. Можете ли вы дать нам больше представления о том, что на самом деле делает код?

Что произойдет, если вы запустите n копий программы как разные процессы Java, причем каждый из них будет использовать только один поток? Если он использует каждый процессор полностью, то, по крайней мере, мы знаем, что это не может быть проблемой с ОС. Говоря об ОС, на какой это работает, и на какой JVM? Если вы можете попробовать разные JVM и разные ОС, результаты могут подсказать вам, в чем дело.

1 голос
/ 02 декабря 2008

Также важный момент: какое оборудование вы используете? Например. 4-8 ядер могут означать, что вы работаете на одном из процессоров Suns Niagara. И несмотря на наличие 4-8 ядер, у них меньше FPU с. При вычислении научных данных может случиться так, что FPU является узким местом.

0 голосов
/ 10 декабря 2008

Попробуйте анализатор задержки, который поставляется с JRockit Mission Control. Он покажет вам, что делает процессор, когда ничего не делает, если приложение ожидает ввода-вывода файла, TLA-выборки, выделения объектов, приостановки потока, JVM-блокировки, gc-паузы и т. Д. Вы также можете увидеть переходы например, когда одна нить просыпается другая. Накладные расходы незначительны, 1% или около того.

См. блог для получения дополнительной информации. Инструмент можно бесплатно использовать для разработки, и вы можете скачать его здесь

0 голосов
/ 04 декабря 2008

Вы делаете синхронизацию на каком-то уровне.

Возможно только в системе выделения памяти, включая сборку мусора. Хотя поставщик JVM работал над тем, чтобы свести к минимуму блокировку в этих областях, он не может уменьшить ее до нуля. Возможно, что-то в вашем приложении выдвигает слабое место в этой области.

Принято считать, что «не создавайте свой собственный пул для восстановления памяти, пусть GC работает на вас». Это верно в большинстве случаев, но не по крайней мере в одном фрагменте кода, который я поддерживаю (доказано с помощью профилирования). Возможно, вам нужно каким-то образом переделать распределение объектов.

0 голосов
/ 02 декабря 2008

Вы пытаетесь использовать все возможности ЦП для своих расчетов, но сама ОС также использует ресурсы. Так что имейте в виду, что ОС будет блокировать часть вашего выполнения, чтобы удовлетворить свои потребности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...