3

配列を 2 つのリストに分割する並列アルゴリズムを C# で作成しました。1 つは特定の述語を満たす要素を含み、もう 1 つは述語を満たさない要素を含みます。これは順序保存アルゴリズムです。

以下のように書いていますが、ハードウェアの同時実行性から利益を得る機会を最大化する方法を知りたいです。

    static void TestPLinqPartition(int cnt = 1000000)
    {
        Console.WriteLine("PLINQ Partition");
        var a = RandomSequenceOfValuesLessThan100(cnt).ToArray();
        var sw = new Stopwatch();
        sw.Start();
        var ap = a.AsParallel();
        List<int> partA = null;
        List<int> partB = null;
        Action actionA = () => { partA = (from x in ap where x < 25 select x).ToList(); };
        Action actionB = () => { partB = (from x in ap where !(x < 25) select x).ToList(); };
        Parallel.Invoke(actionA, actionB);
        sw.Stop();

        Console.WriteLine("Partion sizes = {0} and {1}", partA.Count, partB.Count);
        Console.WriteLine("Time elapsed = {0} msec", sw.ElapsedMilliseconds);
    }
4

2 に答える 2

4

リストが非常に長い場合、並列処理はあまり得られません (2x)。代わりに、Parallel.For を使用し、スレッド ローカルTuple<List<int>, List<int>>を並列ループ状態として使用することをお勧めします。Parallel.For API を使用すると、これを簡単に行うことができます。最後に個々のサブリストをマージできます。

このバージョンは恥ずかしいほど並列であり、同期がないため、CPU バス上でコヒーレンシ トラフィックがほとんど発生しません。

編集: すべてのスレッドで共有されている 2 つのリストを使用することはできないことを強調したいと思います。スレッド ローカル リストを使用する必要があります。ConcurrentQueue でさえ、このシナリオには適していません。なぜなら、制限された CPU コヒーレンシ トラフィックを引き起こすインターロック操作を使用するためです。

于 2012-04-06T21:55:43.813 に答える
1

データを小さなセグメントに分割し (たとえば、Partitionerクラスを使用して)、その位置に関連して各パーティションにインデックスを割り当てます。番号が付けられたパーティションごとに、パーティションを 2 つのグループに分割するTaskを作成します。1 つは述語に一致し、もう 1 つは述語に一致しないグループで、2 つのグループを、元のパーティションのインデックスと共に Task として返します。戻り値。次に、すべてのタスクが完了するのを待ってから、.Concat()(実際にすべてのデータをマージするのに時間を浪費するのを防ぐために) インデックスに従って一致するグループを探し、一致しないグループについても同じようにします。相対的なアイテムの順序を維持しながら、この方法で任意の程度の並列処理を実現できるはずです。

于 2012-04-06T23:23:04.100 に答える