【问题标题】:Where to call .AsParallel() in a LINQ query在 LINQ 查询中调用 .AsParallel() 的位置
【发布时间】:2017-03-05 11:04:26
【问题描述】:

问题

在 LINQ 查询中,我可以正确地(如:编译器不会抱怨)像这样调用 .AsParallel():

(from l in list.AsParallel() where <some_clause> select l).ToList();

或者像这样:

(from l in list where <some_clause> select l).AsParallel().ToList();

到底有什么区别?

我尝试过的

official documentation 来看,我几乎总是看到使用的第一种方法,所以我认为这是要走的路。
今天,我尝试自己运行一些基准测试,结果令人惊讶。这是我运行的代码:

var list = new List<int>();
var rand = new Random();
for (int i = 0; i < 100000; i++)
    list.Add(rand.Next());

var treshold= 1497234;

var sw = new Stopwatch();

sw.Restart();
var result = (from l in list.AsParallel() where l > treshold select l).ToList();
sw.Stop();

Console.WriteLine($"call .AsParallel() before: {sw.ElapsedMilliseconds}");

sw.Restart();
result = (from l in list where l > treshold select l).AsParallel().ToList();
sw.Stop();

Console.WriteLine($"call .AsParallel() after: {sw.ElapsedMilliseconds}");

输出

在 49 之前调用 .AsParallel()
在 4 之后调用 .AsParallel()

因此,显然,尽管文档中说了什么,第二种方法要快得多。这里到底发生了什么?

【问题讨论】:

  • 你的机器是单核还是多核?
  • @viveknuna 多核
  • 每次都会给你不同的结果
  • @viveknuna 是的,但区别总是一样的

标签: c# linq parallel-processing plinq


【解决方案1】:

一般来说,使用 AsParallel 的诀窍是确定并行性所节省的成本是否超过并行处理的开销。

当条件很容易评估时,例如您的条件,创建多个并行流并在最后收集它们的结果的开销大大超过了并行执行比较的好处。

当条件计算密集时,尽早调用 AsParallel 会大大加快速度,因为与并行运行多个 Where 计算的好处相比,开销现在很小。

对于计算困难条件的示例,请考虑一种确定数字是否为素数的方法。在多核 CPU 上并行执行此操作将比非并行实现显着改进。

【讨论】:

  • 感谢您的回答,但我仍然不太明白这两个调用之间的区别。你的意思是当我以第二种方式调用 .AsParallel() 时(在查询结束时)我实际上并没有并行化任何东西?
  • @Mahatma 是的,到那时工作已经以顺序模式完成。 LINQ 需要的只是将并行流中的结果收集到单个列表中。
【解决方案2】:

AsParallel 的第二次使用不是必须的,它不影响 some_clause。

另见下面的测试代码:

[TestMethod]
public void Test()
{
    var items = Enumerable.Range(0, 10);
    int sleepMs;
    for (int i = 0; i <= 4; i++)
    {
        sleepMs = i * 25;
        var elapsed1 = CalcDurationOfCalculation(() => items.AsParallel().Select(SomeClause).ToArray());
        var elapsed2 = CalcDurationOfCalculation(() => items.Select(SomeClause).AsParallel().ToArray());

        Trace.WriteLine($"{sleepMs}: T1={elapsed1} T2={elapsed2}");
    }

    long CalcDurationOfCalculation(Action calculation)
    {
        var watch = new Stopwatch();
        watch.Start();
        calculation();
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }

    int SomeClause(int value)
    {
        Thread.Sleep(sleepMs);
        return value * 2;
    }
}

和输出:

0: T1=77 T2=11
25: T1=103 T2=272
50: T1=202 T2=509
75: T1=303 T2=758
100: T1=419 T2=1010

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 1970-01-01
    • 2021-11-23
    • 2023-03-03
    • 1970-01-01
    • 2018-09-30
    相关资源
    最近更新 更多