Я пытаюсь выяснить, почему параллельный foreach не дает ожидаемого ускорения на машине с 32 физическими ядрами и 64 логическими ядрами при простом тестовом вычислении.
...
var parameters = new List<string>();
for (int i = 1; i <= 9; i++) {
parameters.Add(i.ToString());
if (Scenario.UsesParallelForEach)
{
Parallel.ForEach(parameters, parameter => {
FireOnParameterComputed(this, parameter, Thread.CurrentThread.ManagedThreadId, "started");
var lc = new LongComputation();
lc.Compute();
FireOnParameterComputed(this, parameter, Thread.CurrentThread.ManagedThreadId, "stopped");
});
}
else
{
foreach (var parameter in parameters)
{
FireOnParameterComputed(this, parameter, Thread.CurrentThread.ManagedThreadId, "started");
var lc = new LongComputation();
lc.Compute();
FireOnParameterComputed(this, parameter, Thread.CurrentThread.ManagedThreadId, "stopped");
}
}
}
...
class LongComputation
{
public void Compute()
{
var s = "";
for (int i = 0; i <= 40000; i++)
{
s = s + i.ToString() + "\n";
}
}
}
Выполнение функции Compute занимает около 5 секунд. Мое предположение состояло в том, что с параллельным циклом foreach каждая дополнительная итерация создает параллельный поток, работающий на одном из ядер и берущий столько, сколько потребуется для вычисления функции Compute только один раз. Итак, если я запущу цикл дважды, то с последовательным foreach это займет 10 секунд, а с параллельным foreach всего 5 секунд (при условии, что доступно 2 ядра). Ускорение будет 2. Если я запущу цикл три раза, то с последовательным foreach это займет 15 секунд, но снова с параллельным foreach всего 5 секунд. Ускорение будет 3, затем 4, 5, 6, 7, 8 и 9. Однако то, что я наблюдаю, является постоянным ускорением 1,3.
Ускорение, деление времени последовательного foreach на параллельное foreach
Событие, запущенное в FireOnParameterComputed, предназначено для использования в индикаторе выполнения GUI для отображения хода выполнения. В индикаторе выполнения хорошо видно, что для каждой итерации создается новый поток.
Мой вопрос: почему я не вижу ожидаемого ускорения или, по крайней мере, близкого к ожидаемому ускорению?
Compute
занимает 5 секунд, запуск его 4 раза параллельно на четырехъядерной машине все равно займет 5 секунд. Если бы вы не использовалиParallel.Foreach
, это заняло бы 20 минут. - person Panagiotis Kanavos   schedule 24.10.2018Parallel.For/ForEach
имеет смысл внутриCompute
.Compute
должен содержатьParallel.For/ForEach
вместо цикла. Это будет разделять входные данные на столько разделов, сколько ядер (примерно) и использовать одну задачу для обработки каждого раздела на полной скорости. Для 4 ядер это может привести к улучшению на 400%. Это не то, что делает код вопросов, это почти как если бы он пытался использоватьParallel.ForEach
какTask.Run
для одновременного запуска нескольких экземпляровCompute
- person Panagiotis Kanavos   schedule 25.10.2018