我写了一些基本的示例代码来熟悉PLINQ。
我遇到了一些奇怪的东西。我不知道我的代码中是错误还是我对PLINQ的理解错误。
MSDN文档指出,添加AsOrdered()将以可能的性能成本保留调用的顺序。
我写了一些单元测试,并注意到文档中所述的对结果集的顺序的影响。但我已经看到了对性能的负面影响。
以下是我的方法:
public IEnumerable<int> ParallelCalculatePrimesUpTo(int maxValue)
{
return from number in Enumerable.Range(1, maxValue).AsParallel()
where IsPrime(number)
select number;
}
public IEnumerable<int> OrderedParallelCalculatePrimesUpTo(int maxValue)
{
return from number in Enumerable.Range(1, maxValue).AsParallel().AsOrdered()
where IsPrime(number)
select number;
}
我非常简单的基准
[TestMethod]
public void SimplisticBenchmark6()
{
var primeNumberCalculator = new PrimeNumberCalculator();
var startTime = DateTime.Now;
primeNumberCalculator.ParallelCalculatePrimesUpTo(10000000).ToList();
var totalTime = DateTime.Now - startTime;
Console.WriteLine(totalTime);
}
[TestMethod]
public void SimplisticBenchmark7()
{
var primeNumberCalculator = new PrimeNumberCalculator();
var startTime = DateTime.Now;
primeNumberCalculator.OrderedParallelCalculatePrimesUpTo(10000000).ToList();
var totalTime = DateTime.Now - startTime;
Console.WriteLine(totalTime);
}
无论我多久运行一次这个测试,有序版本都会打败无序版本。我的四核计算机上的订购速度大约快4秒。对于有序的一个,我得到大约18秒,对于无序的一个,我得到22秒。我在两天的时间内完成了几十次测试(在那些日子之间重新启动)。
如果我将数字10 000 000减至6 000 000,差异仍然存在,但不太明显,如果我将其降低至3 000 000,则速度大致相同。
我尝试按执行顺序运行测试,结果是一样的。
以下是在PLINQ查询中调用的IsPrime方法:
// uses inneficient trial division algorithm
private bool IsPrime(int number)
{
if (number == 1)
return false;
for (int divisor = 2; divisor <= Math.Sqrt(number); divisor++)
{
if (number % divisor == 0)
return false;
}
return true;
}
是什么解释了这个?
答案 0 :(得分:4)
您是否始终以相同的顺序运行测试?
我在我的机器上重新创建了你的结果,我发现,无论如何,'Ordered'结果更快。我使用稍微修改过的代码进行基准测试:
static void Main(string[] args)
{
const int size = 9000000;
BenchIt("Parallel", ParallelCalculatePrimesUpTo, size);
BenchIt("Ordered ", OrderedParallelCalculatePrimesUpTo, size);
Console.ReadKey();
}
public static void BenchIt(string desc, Func<int, IEnumerable<int>> myFunc, int size)
{
var sw = new Stopwatch();
sw.Restart();
myFunc.Invoke(size).ToList();
sw.Stop();
Console.WriteLine("{0} {1}",desc, sw.Elapsed);
}
我的结果最初表明你是对的。有序的方法更快。但是,如果我SWAPPED调用的顺序,我发现非有序方法更快。换句话说,无论哪一个更快。据推测,由于任务并行库正在进行的线程池管理。
但是 - 我机器上两者之间的差异非常小。远远没有你看到的差异。
您的硬件是什么样的?
PLINQ做了一些关于如何执行最快的猜测。在这种情况下,我不知道这是否会直接帮助你;但是你可能想在IsPrime中间设置一个断点并在几百次迭代后停止它并检查线程窗口。
执行ParallelCalculatedPrimesUpTo
经文OrderedParallelCalculatePrimesUpTo
时有多少个主题?我到了这里;但它可能会决定您机器上的不同值,从而产生您所看到的意外时间。在我的机器上 - 我每次都得到8个线程 - 但我的时间几乎完全相同 - 无论哪个被称为第一个都因为这些线程的创建而变慢。但是你不能保证特定数量的线程(你可以设置最大值,但不能强制它们被使用)。
答案 1 :(得分:1)
您能告诉我们4个不同内核的CPU利用率是多少? AsOrdered()可能会强制在同一个核心上发生更多顺序调用。通过改进的局部性,硅级缓存和分支预测可能对您有利。
另一种可能性是,在使用AsOrdered()投影时,对于单调递增整数(int.Range)的情况,.NET框架中有一些优化。我不确定这是怎么回事,但这是可能的。
一个有趣的比较测试是以随机顺序生成第三组数字(显然,你必须提前将它们随机化,然后再处理三个数组)。然后看看它是否与它有关?