Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is PLINQ slower than for loop?

Let's say I have these two methods:

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;
 }

The following were the results I got:

PFactorial(25000) -> 0,9897 seconds
Factorial(25000) -> 0,9252 seconds

I understand that PLINQ has some overhead because of the threads setup, but with such a big n I was expecting PLINQ to be faster.

Here is another result:

PFactorial(50000) -> 4,91035 seconds
Factorial(50000) -> 4,40056 seconds
like image 908
Matias Cicero Avatar asked Oct 26 '15 19:10

Matias Cicero


1 Answers

Parallel is not really possible with aggregate. At least i cant imagine that in my mind. Any way, You should make parallelization possible by dividing your list into chunks. Find those results. and finally multiply chunks. Here is the fast way of 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;
}

Test

PFactorial(50000) -> 1,41 seconds
Factorial(50000) -> 2,69 seconds

Edit: As mentioned by Servy and Chatzigiannakis if you dont use seed it can perfectly use parallelization and you get pretty much same results as above (a bit faster).

return Enumerable.Range(1,n).Select(x => (BigInteger)x).AsParallel().Aggregate(BigInteger.Multiply);
like image 127
M.kazem Akhgary Avatar answered Oct 25 '22 08:10

M.kazem Akhgary