4

PLINQの例を実装しようとしていますが、次の問題に直面しています。シーケンシャルクエリはパラレルクエリよりも高速に実行されます。

コード例は次のとおりです。

        Stopwatch sw = new Stopwatch();

        int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray();

        sw.Start();
        int[] x1 = vals.Where(x => x % 2 == 0).ToArray();
        sw.Stop();
        Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds);


        sw.Restart();
        int[] x2 = vals.AsParallel().Where(x => x % 2 == 0).ToArray();
        sw.Stop();
        Console.WriteLine("Parallel Execution {0} milliseconds", sw.ElapsedMilliseconds);

私のマシンはPentium(R)Dual-Coreです。Quad-CoreAMD Opteron(tm)も試しました。

同じ結果並列クエリは順次よりも実行が遅くなります。私の問題は何ですか?

ありがとう。

4

3 に答える 3

5

これはオーバーヘッドと関係があると思います。繰り返すコレクションは非常に小さく(32kショーツ)、これらのアイテムに対して実行される操作は簡単です。

このようなシナリオでは、コレクションの分割、フィルタリング、および再マージは、単一の反復内で同じことを実行するよりもはるかにコストがかかる可能性があります。

比較に費用がかかり(文字列の検索など)、コレクションが増えると、結果が変化することがわかります。

于 2012-06-06T14:36:04.567 に答える
4

あなたの「問題」は、意味がないときにPLINQを使用しています

PLINQが常に高速になるとは限りません。PLINQは常にオーバーヘッドを追加します。

CPU命令に関して; 実行する必要のある作業の量(Xと呼びます)が何であれ、Xを超える命令を実行することになります。PLINQは、スレッドを開始し、スレッドに作業を委任し、結果を作業可能な形式に戻すために、多くの追加作業を実行します。

これを行うことの利点は、複数のCPU/コアで作業できることです。時々それはより速いです。実行しているCPU作業の量がオーバーヘッドに比べて少ない場合、処理速度は遅くなります。

コードを実行すると、次の出力が得られます。

順次実行2ミリ秒

並列実行40ミリ秒

また、PLINQコードによって作成されている8つのワーカースレッドを確認できます。これらの8つのスレッドは、2ミリ秒に相当する計算で多くのオーバーヘッドを表します。Parallel Executionベンチマークを2回実行すると、オーバーヘッドがどれだけあるかを知ることができます。ワーカースレッドはぶらぶらします。これが2回目の実行での私の出力です:

順次実行2ミリ秒

並列#1実行40ミリ秒

並列#2実行3ミリ秒

2回目ははるかに高速です。しかし、それでも何もしないよりも遅いです。なぜなら、ワーカースレッドが既に作成されている場合でも、PLINQは、スレッド間で操作を分割し、アクセス可能な形式で結果を取得するための作業を行う必要があるためです。

実行する必要のある作業が多いほど、オーバーヘッドの影響は少なくなります。この例では、Whereラムダを呼び出された静的関数に置き換えIsValid、%2を1回だけではなく500回計算します。

static bool IsValid(int input)
{
    int result=0;

    for(int i =0;i<500;i++)            
        result = input%2;

    return result == 0;
}

今-私の実行時間は次のとおりです。

順次実行36ミリ秒

並列#1実行47ミリ秒

並列#2実行9ミリ秒

PLINQは、最初の実行ではまだ低速ですが、2番目の実行では大幅に高速であることがわかります。ループを500から5000に増やすことでCPUの作業を増やす場合(私のマシンでは)、PLINQが勝ちます。

TL;DR-あなたは正しくやっています。PLINQをより速く選択するのに十分な作業を行っていないだけです。

これが私がやったことの全体のソースコードです:

static void Main(string[] args)
{
    Stopwatch sw = new Stopwatch();

    int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray();

    sw.Start();
    int[] x1 = vals.Where(IsValid).ToArray();
    sw.Stop();
    Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds);

    sw.Restart();
    int[] x2 = vals.AsParallel().Where(IsValid).ToArray();
    sw.Stop();
    Console.WriteLine("Parallel #1 Execution {0} milliseconds", sw.ElapsedMilliseconds);

    sw.Restart();
    int[] x3 = vals.AsParallel().Where(IsValid).ToArray();
    sw.Stop();
    Console.WriteLine("Parallel #2 Execution {0} milliseconds", sw.ElapsedMilliseconds);

    Console.Read();
}

static bool IsValid(int input)
{
    int result=0;

    for(int i =0;i<5000;i++)            
        result = input%2;

    return result == 0;
}
于 2012-06-06T15:41:09.443 に答える
2

これはうまくいくようです:

        Stopwatch sw = new Stopwatch();

        int[] vals = Enumerable.Range(0, 10000000).ToArray();

        sw.Start();
        var x1 = vals.Where(x => x % 2 == 0).ToList();
        sw.Stop();
        Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds);


        sw.Restart();
        var x2 = vals.Where(x => x % 2 == 0).AsParallel().ToList();
        sw.Stop();
        Console.WriteLine("Parallel Execution {0} milliseconds", sw.ElapsedMilliseconds);

200個の値に対して別のスレッドを開始しないでください。単一のスレッドでループ全体を終了するよりも、他のスレッドを開始/ウェイクアップする方が時間がかかります。+より多くのスレッドは、スレッド同期メカニズムを意味します。

LE:わかりました。Int16.MaxValueを試してみましたが、そこでうまく機能します。最大値が約30kであることに気づかなかったため、コメントがあなたのケースに当てはまらない可能性があります。おそらく問題は、AsParralelが置き忘れられたことです。

于 2012-06-06T14:35:21.690 に答える