Почему PLINQ медленнее, чем для цикла?
Скажем, у меня есть два метода:
public BigInteger PFactorial(int n)
{
return Enumerable.Range(1, n)
.AsParallel()
.Select(i => (BigInteger)i)
.Aggregate(BigInteger.One, BigInteger.Multiply);
}
public BigInteger Factorial(int n)
{
BigInteger result = BigInteger.One;
for(int i = 1; i <= n; i++)
result *= i;
return result;
}
Были получены следующие результаты:
PFactorial(25000) -> 0,9897 seconds
Factorial(25000) -> 0,9252 seconds
Я понимаю, что у PLINQ есть некоторые накладные расходы из-за установки потоков, но с таким большим n
я ожидал, что PLINQ будет быстрее.
Вот еще один результат:
PFactorial(50000) -> 4,91035 seconds
Factorial(50000) -> 4,40056 seconds
Ответы
Ответ 1
Параллельно с агрегатом невозможно. По крайней мере, я не могу представить это, на мой взгляд. В любом случае, Вы должны сделать распараллеливание возможным, разделив ваш список на куски. Найдите эти результаты. и, наконец, размножать куски. Вот быстрый способ PLinq.
static public BigInteger PFactorial(int n)
{
var range = Enumerable.Range(1, n).Select(x => (BigInteger) x).AsParallel();
var lists = range.GroupBy(x => x/(n/Environment.ProcessorCount)).Select(x => x.AsEnumerable());
var results = lists.Select(x => x.Aggregate(BigInteger.One, BigInteger.Multiply));
var result = results.Aggregate(BigInteger.One, BigInteger.Multiply);
return result;
}
Тест
PFactorial(50000) -> 1,41 seconds
Factorial(50000) -> 2,69 seconds
Изменить: Как упоминалось Servy и Chatzigiannakis, если вы не используете семена, он может прекрасно использовать распараллеливание, и вы получите почти такие же результаты, как и выше (немного быстрее).
return Enumerable.Range(1,n).Select(x => (BigInteger)x).AsParallel().Aggregate(BigInteger.Multiply);
Ответ 2
Не предполагайте, что pLinQ всегда быстрее LinQ. Время выполнения PLinQ на основе многих условий
Используйте PLinQ только тогда, когда есть больше элементов и у вас есть запрос на интенсивность процессора. Я предлагаю использовать System.Threading.Thread.Sleep(1)
в функции для эмуляции нагрузки процессора как задержки цикла, а затем вызвать эту функцию из LinQ и PlinQ в 10000 раз. Тогда вы можете увидеть разницу. Пожалуйста, найдите образец здесь
Ваша текущая функция Factorial на самом деле не делает какой-либо ЦП интенсивной задачи и причины PLinQ занимает больше времени, поскольку он погрузил запрос в несколько ядер и консолидировал результат отдельного ядра для одиночного вывода, что занимает немного больше времени, чем обычно.
Также убедитесь, что вы используете многоядерные процессоры (минимум 4 даст вам хороший анализ)