在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}");
输出
之前调用.AsParallel():49
调用.AsParallel()之后:4
所以,显然,尽管文档说的是,第二种方法要快得多。这到底发生了什么?
答案 0 :(得分:7)
一般来说,使用AsParallel的技巧是决定并行性的节省是否超过了并行处理的开销。
当条件易于评估时,例如你的条件,制作多个并行流并在最后收集结果的开销大大超过了并行执行比较的好处。
当条件计算量很大时,早期调用AsParallel会加快速度,因为与并行运行多个Where计算的好处相比,开销现在很小。
对于计算困难条件的示例,请考虑一种决定数字是否为素数的方法。在多核CPU上并行执行此操作将显着改善非并行化实现。
答案 1 :(得分:1)
第二次使用 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