为什么 PLINQ 比 for 循环慢?
Why is PLINQ slower than for loop?
假设我有这两种方法:
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
聚合实际上不可能并行。至少我无法想象。无论如何, 您应该通过将列表分成块来使并行化成为可能。找到那些结果。最后乘以块。这是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);
请不要假设 pLinQ 总是比 LinQ 快。基于多种条件的 PLinQ 执行时间
仅当有更多元素并且有一些 CPU 密集查询时才使用 PLinQ。我建议在函数中使用 System.Threading.Thread.Sleep(1)
来模拟 CPU 负载作为周期延迟,然后从 LinQ 和 PlinQ 调用该函数 10000 次。然后你就可以看到区别了。请找到 sample here
您当前的函数 Factorial 实际上没有执行任何 CPU 密集型任务,这也是 PLinQ 花费更多时间的原因,因为它将查询转移到多核中的 运行 并将单个核的结果合并为单个核输出,比正常情况花费更多的时间。
还要确保你使用的是多核处理器(最少 4 个会给你很好的分析)
假设我有这两种方法:
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
聚合实际上不可能并行。至少我无法想象。无论如何, 您应该通过将列表分成块来使并行化成为可能。找到那些结果。最后乘以块。这是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);
请不要假设 pLinQ 总是比 LinQ 快。基于多种条件的 PLinQ 执行时间
仅当有更多元素并且有一些 CPU 密集查询时才使用 PLinQ。我建议在函数中使用 System.Threading.Thread.Sleep(1)
来模拟 CPU 负载作为周期延迟,然后从 LinQ 和 PlinQ 调用该函数 10000 次。然后你就可以看到区别了。请找到 sample here
您当前的函数 Factorial 实际上没有执行任何 CPU 密集型任务,这也是 PLinQ 花费更多时间的原因,因为它将查询转移到多核中的 运行 并将单个核的结果合并为单个核输出,比正常情况花费更多的时间。
还要确保你使用的是多核处理器(最少 4 个会给你很好的分析)