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 秒かかります。私は 2 日間にわたって何十回もテストを実行しました (その間に再起動を行いました)。

数値を 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);
}

私の結果は、最初、あなたが正しいことを示しました。順序付けされた方法の方が高速でした。ただし、呼び出しの順序を入れ替えると、順序付けされていないメソッドの方が高速であることがわかりました。言い換えれば、2番目に進んだ方が速かったということです。おそらく、タスク並列ライブラリが実行しているスレッドプール管理が原因です。

しかし、私のマシンでの2つの違いは非常に小さかった。あなたが見た違いの量にはほど遠い。

あなたのハードウェアはどのように見えますか?

PLINQは、最速で実行する方法についていくつかの推測を行います。この場合、これが直接役立つかどうかはわかりません。ただし、IsPrimeの途中にブレークポイントを設定し、数百回の反復後にブレークポイントで停止して、スレッドウィンドウを調べることをお勧めします。

ParallelCalculatedPrimesUpTo詩を実行するとき、あなたはいくつのスレッドを持っていますOrderedParallelCalculatePrimesUpToか?私はここに到達しています。ただし、マシン上のさまざまな値を決定しているために、予期しない時間が発生している可能性があります。私のマシンでは、毎回8つのスレッドを取得しますが、時間はほぼ同じです。これらのスレッドが作成されるため、最初に呼び出されたスレッドの方が遅くなります。ただし、特定のスレッド数が保証されるわけではありません(最大数を設定することはできますが、強制的に使用させることはできません)。

于 2012-06-06T10:23:09.100 に答える
1

4 つの異なるコアの CPU 使用率を教えてください。AsOrdered() が同じコアでより多くの順次呼び出しを強制している可能性があります。ローカリティが向上すると、シリコンレベルのキャッシュと分岐予測が有利に機能する可能性があります。

もう 1 つの可能性は、AsOrdered() プロジェクションを使用するときに単調に増加する整数 (int.Range) の場合に備えて、.NET フレームワークにいくつかの最適化があることです。それがどのように機能するかはわかりませんが、可能です。

比較のための興味深いテストは、ランダムな順序で 3 番目の数値セットを生成することです (明らかに、事前にランダム化してから 3 つの配列を処理する必要があります)。それが何か関係があるかどうか見てみましょう。

于 2012-06-06T02:58:45.510 に答える