6

我写了一些基本的示例代码来熟悉 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;
}

这是什么解释?

4

2 回答 2

4

您是否总是以相同的顺序运行测试?

我已经在我的机器上重新创建了您的结果,我发现无论如何,“有序”结果更快。我使用稍微修改的代码进行基准测试:

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

我的结果表明,最初,你是对的。有序方法更快。但是,如果我交换了调用的顺序,我发现无序的方法更快。换句话说,无论哪一秒都更快。大概是因为任务并行库正在做的线程池管理。

但是 - 我的机器上两者之间的差异非常小。远不及你看到的差异量。

你的硬件是什么样的?

PLINQ 会猜测如何以最快的速度执行。我不知道在这种情况下这是否会直接帮助您;但是您可能想在 IsPrime 的中间设置一个断点并在几百次迭代后停止并检查线程窗口。

ParallelCalculatedPrimesUpTo执行verse时有多少个线程OrderedParallelCalculatePrimesUpTo?我到了这里;但它可能会决定您机器上的不同值,从而产生您所看到的意外时间。在我的机器上 - 我每次都有八个线程 - 但我的时间几乎相同 - 由于这些线程的创建,首先调用的线程会更慢。但是您不能保证特定数量的线程(您可以设置最大值,但不能强制使用它们)。

于 2012-06-06T10:23:09.100 回答
1

您能告诉我们 4 个不同内核的 CPU 利用率是多少吗?AsOrdered() 可能会迫使更多的顺序调用发生在同一个核心上。随着局部性的提高,硅级缓存和分支预测可能对您有利。

另一种可能性是 .NET 框架在使用 AsOrdered() 投影时针对单调递增整数 (int.Range) 的情况进行了一些优化。我不确定这将如何工作,但这是可能的。

一个有趣的比较测试是以随机顺序生成第三组数字(显然,您必须提前将它们随机化,然后处理三个数组)。然后看看有没有关系?

于 2012-06-06T02:58:45.510 回答