2015-10-26 145 views
10

比方说,我有两种方法:为什么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有,因为线程设置的一些开销,但这么大的我期待PLINQ更快。

这里是另外一个结果是:

PFactorial(50000) -> 4,91035 seconds 
Factorial(50000) -> 4,40056 seconds 
+4

这就是为什么我们的标杆。不要猜测并行化某件事会获得多少收益,而应该测试一下,所以你肯定知道。当然,请记住,聚合可以并行,但并不像其他类型的操作(如预测)那样有效。 – Servy

+3

'Aggregate'不会并行发生。 – Ryan

+0

'Enumerable.Range'也会造成一些额外的时间... –

回答

10

并行是不是真的有可能与汇总。至少我无法想象在我的脑海里。任何方式,您应该通过将列表分成块来实现并行化。找到那些结果。最后再乘以大块。这是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); 
+1

'Aggregate'''绝对可以并行执行它的工作,而不是在有种子的时候。当然,它的工作方式与您所做的完全一致,即将工作分解为块,并行汇总,然后梳理中间结果。 – Servy

+0

我尝试过不使用种子。我仍然得到与正常阶乘相同的结果。任何方式感谢信息。我看着它。 @Servy –

+1

'ParallelEnumerable.Aggregate'在内部区分关联和非关联聚合函数,并行运行前者。当没有说明时,我认为它假设了联想(和交换)。 –

1

请不要以为pLinQ总是比LinQ快。基于许多条件的PLinQ执行时间

只有当有更多元素并且有一些CPU密集型查询时才使用PLinQ。我建议在函数中使用System.Threading.Thread.Sleep(1)来模拟CPU负载作为周期延迟,然后从LinQ和PlinQ调用该函数10000次。然后你可以看到差异。请发现sample here

您当前的函数因为实际上没有执行任何CPU密集型任务和原因PLinQ需要更多的时间,因为它会使查询在多个核心中运行,并将单个核心的结果合并到单个输出中,时间然后正常。

还要确保您使用的是多核心处理器(最低4会给你很好的分析)

+0

*“您当前的函数Factorial实际上没有执行任何CPU密集型任务”*是的,是的。 – Ryan

+0

@minitech它不是 - 它只是将一些数字相乘,看起来平均(4900ms/50000)0.098ms乘法。为了实现任何收益,特别是要考虑所有工作,以确定批量,启动线程,处理所有包装等,这不是一项伟大的任务。 – NPSF3000

+2

@ NPSF3000:经公认的答案证明,它可以实现收益。 – Ryan