为什么PLINQ慢于循环?

时间:2015-10-26 19:08:40

标签: c# linq task-parallel-library

假设我有这两种方法:

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

2 个答案:

答案 0 :(得分: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 :(得分:1)

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

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

您当前的功能Factorial实际上没有执行任何CPU密集型任务并且导致PLinQ花费更多时间,因为它让查询在多个核心中运行并将单个核心的结果合并到单个输出,这比平时花费更多的时间。

还要确保使用多核处理器(最少4个会给你很好的分析)