Многопоточное приложение в многоядерной среде - странная нагрузка на ядро - PullRequest
4 голосов
/ 07 сентября 2011

В данной среде: процессор Xeon с 16 ядрами, ОС - Win 2008 server R2.

Данное приложение (.Net / C #) перед распараллеливанием загружает 1 ядро ​​почти на 100%. Очевидным решением для получения прибыли было использование библиотеки параллельных задач .Net 4 для ускорения работы приложений в X раз. Предположим, что параллельная часть приложения является действительно подходящей - между потоками не происходит блокировка (нет общих ресурсов, каждая параллельная задача полностью независима). Но, к моему сожалению, прибыль действительно низкая - 16-поточное приложение работает ок. В 2 раза быстрее, чем последовательный.

Вот первая иллюстрация - 16 потоков на 16 ядрах

16 http://s57.radikal.ru/i157/1109/48/e1df69e34c43.jpg

Это кажется действительно странным - каждая задача одинакова, но первые 8 ядер загружены практически на одном уровне (~ 30%), а остальные 8 имеют постепенно убывающую нагрузку.

Итак, я пробовал разные конфигурации, например, 8 потоков на 16 ядрах

8 http://s45.radikal.ru/i109/1109/be/148f110a973d.jpg

Похоже, что все 8 потоков запущены на 8 ядрах, и потоки не переносятся из одного ядра в другое. Более того, на 8 ядрах средняя нагрузка на ядро ​​выше, чем на 16.

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

Итак, основные тенденции, которые я не могу объяснить, - чем больше потоков, тем ниже средняя нагрузка на ядро, а интегральное использование процессора составляет максимум 20-25%. И каждая операция в потоке выполняется медленнее с ростом количества потоков.

Есть идеи, чтобы объяснить эти странные вещи?

UPD

После применения Server GC картина значительно изменилась

8 потоков на 16 ядрах Иллюстрация:

8AfterServerGC http://s004.radikal.ru/i207/1109/e8/94d0ce0e97f3.bmp

12 потоков на 16 ядрах Иллюстрация:

12AfterServerGC http://i025.radikal.ru/1109/9f/694544cf992e.bmp

15 потоков на 16 ядрах Иллюстрация:

15AfterServerGC http://i003.radikal.ru/1109/b2/82033ad2eadf.bmp

Итак, похоже, что использование процессора увеличивается с ростом числа ядер. Первое, что меня беспокоит, это то, что все ядра используются и потоки переходят от ядра к ядру, поэтому общая производительность не так хороша.

Во-вторых, максимальная скорость приложения составляет 12 ядер, 15 ядер дают одинаковые результаты, 16 ядер даже медленнее.

Какая возможная причина?

Ответы [ 2 ]

2 голосов
/ 07 сентября 2011

Шаблон, который вы видите, часто указывает на узкое место ввода / вывода.Если ваши диски или сеть работают на полную мощность, чтобы предоставить данные для этих вычислений (или обработать результаты), то вы можете запустить их на миллионах ядер без каких-либо дополнительных преимуществ.Я бы посоветовал использовать Sysinternals Process Explorer для проверки сетевых и дисковых операций ввода-вывода и выяснения, существует ли там проблема, прежде чем пытаться разобраться, почему это плохо распараллеливается.

1 голос
/ 07 сентября 2011

Поскольку похоже, что у вас нет внутренней синхронизации с вашим методом, проблема, вероятно, связана с разделением.

Учитывая, что вы используете TPL, работа должна отправляться ядрам на основе разделителя. Однако фактический источник IEnumerable<T> не является потокобезопасным, поэтому требуется доступ через одно ядро. По сути, это часто приводит к характеристикам производительности, подобным приведенным выше, если фактическая работа мала по сравнению с количеством элементов.

Обходной путь - использовать класс Partitioner для предварительного разбиения ваших рабочих элементов на блоки, а затем проходить итерации по «блокам» элементов параллельно. Подробнее см. Как: ускорить работу небольших петлевых тел .

...