16

並列化したい単純なLinQ-to-objectクエリを作成していますが、ステートメントの順序が重要かどうか疑問に思っていますか?

例えば

IList<RepeaterItem> items;

var result = items
        .Select(item => item.FindControl("somecontrol"))
        .Where(ctrl => SomeCheck(ctrl))
        .AsParallel();

対。

var result = items
        .AsParallel()
        .Select(item => item.FindControl("somecontrol"))
        .Where(ctrl => SomeCheck(ctrl));

違いはありますか?

4

2 に答える 2

24

絶対。最初のケースでは、投影とフィルタリングが直列に実行され、その後、すべてが並列化されます。

2番目のケースでは、投影とフィルタリングの両方が並行して行われます。

最初のバージョンを使用する特別な理由がない限り(たとえば、プロジェクションにスレッドアフィニティがある場合、またはその他の奇妙な場合)、2番目のバージョンを使用する必要があります。

編集:ここにいくつかのテストコードがあります。多くのベンチマークに欠陥がありますが、結果はかなり決定的です。

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;

class Test
{
    static void Main()
    {
        var query = Enumerable.Range(0, 1000)
                              .Select(SlowProjection)
                              .Where(x => x > 10)
                              .AsParallel();
        Stopwatch sw = Stopwatch.StartNew();
        int count = query.Count();
        sw.Stop();
        Console.WriteLine("Count: {0} in {1}ms", count,
                          sw.ElapsedMilliseconds);

        query = Enumerable.Range(0, 1000)
                          .AsParallel()
                          .Select(SlowProjection)
                          .Where(x => x > 10);
        sw = Stopwatch.StartNew();
        count = query.Count();
        sw.Stop();
        Console.WriteLine("Count: {0} in {1}ms", count,
                          sw.ElapsedMilliseconds);
    }

    static int SlowProjection(int input)
    {
        Thread.Sleep(100);
        return input;
    }
}

結果:

Count: 989 in 100183ms
Count: 989 in 13626ms

現在、PFXでは多くのヒューリスティックな処理が行われていますが、最初の結果がまったく並列化されていないのに対し、2番目の結果は並列化されていることは明らかです。

于 2011-02-16T09:12:36.377 に答える
2

それはパフォーマンスだけでなく重要です。1番目と2番目のクエリの結果は等しくありません。並列処理を行い、元の順序を維持するためのソリューションがあります。を使用しAsParallel().AsOrdered()ます。3番目のクエリはそれを示しています。

var SlowProjection = new Func<int, int>((input) => { Thread.Sleep(100); return input; });

var Measure = new Action<string, Func<List<int>>>((title, measure) =>
{
    Stopwatch sw = Stopwatch.StartNew();
    var result = measure();
    sw.Stop();
    Console.Write("{0} Time: {1}, Result: ", title, sw.ElapsedMilliseconds);
    foreach (var entry in result) Console.Write(entry + " ");         
});

Measure("Sequential", () => Enumerable.Range(0, 30)
    .Select(SlowProjection).Where(x => x > 10).ToList());
Measure("Parallel", () => Enumerable.Range(0, 30).AsParallel()
    .Select(SlowProjection).Where(x => x > 10).ToList());
Measure("Ordered", () => Enumerable.Range(0, 30).AsParallel().AsOrdered()
    .Select(SlowProjection).Where(x => x > 10).ToList());

結果:

Sequential Time: 6699, Result: 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Parallel Time: 1462, Result: 12 16 22 25 29 14 17 21 24 11 15 18 23 26 13 19 20 27 28
Ordered Time: 1357, Result: 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

驚いたのですが、10回以上のテスト実行後も結果は一貫していました。少し調べてみたところ、.Net4.0では「バグ」であることが判明しました。4.5では、AsParallel()はAsParallel()。AsOrdered()より遅くありません。

参照はここにあります:

http://msdn.microsoft.com/en-us/library/dd460677(v=vs.110).aspx

于 2014-01-23T09:44:30.797 に答える