简体   繁体   English

有一种简单的方法可以使用非交换操作进行并行聚合吗?

[英]Is there an easy way to do parallel aggregation with a non-commutative operation?

.NET Framework makes it easy to do parallel aggregation, but according to the documentation , it works for commutative operations only, that is an operation where f ( x , y ) = f ( y , x ): .NET Framework使得并行聚合变得容易,但根据文档 ,它仅适用于可交换操作,即fxy )= fyx )的操作:

The .NET implementation of the Parallel Aggregation pattern also expects the operations to be commutative. 并行聚合模式的.NET实现也期望操作是可交换的。

I want to aggregate string values using a concatenation, that is a non-commutative operation. 我想使用串联聚合字符串值,这是一种非交换操作。 A sequential approach looks like this: 顺序方法如下所示:

var result = string.Concat(sequence.Select(this.LongOperation));

so if this.LongOperation returns successively Hello , World and ! 所以如果this.LongOperation连续回来HelloWorld! , the final result is HelloWorld! ,最终结果是HelloWorld! .

If I use parallel aggregation, the result could be HelloWorld , but also World!Hello , !HelloWorld , etc. 如果我使用并行聚合,结果可能是HelloWorld ,也可能是World!Hello!HelloWorld等。

A workaround would be to do something similar to: 解决方法是做类似的事情:

var result = sequence
    .AsParallel()
    .Select((v, i) => new { Index = i, Value = v })
    .Select(c => new { Index = c.Index, Value = this.LongOperation(c.Value))
    .OrderBy(c => c.Index)
    .Aggregate(seed: string.Empty, func: (prev, current) => prev + current);

with the (unimportant, in my particular case) drawback that the entire sequence will be evaluated at OrderBy step anyway, without waiting until the aggregation. 与(不重要的,在我的特定情况下)缺点一样,整个序列将在OrderBy步骤中进行评估,而不必等到聚合。 Another way to write this is: 另一种写这个的方法是:

var parts = sequence
    .AsParallel()
    .Select((v, i) => new { Index = i, Value = v })
    .Select(c => new { Index = c.Index, Value = this.LongOperation(c.Value))
    .OrderBy(c => c.Index)
    .Select(c => c.Value);

var result = string.Concat(parts);

Am I expected to do that, or is there a simpler way to do the thing? 我希望这样做,还是有更简单的方法来做这件事?

You're looking for ParallelEnumerable.AsOrdered : 您正在寻找ParallelEnumerable.AsOrdered

var result = sequence
    .AsParallel()
    .AsOrdered()
    .Aggregate(seed: string.Empty, func: (prev, current) => prev + current);

The fact that you need to preserve ordering will have a performance hit on your query. 您需要保留排序的事实将对您的查询产生性能影响。 As the results need to be aggregated in order, you won't be enjoying the maximum benefit of parallelism, and may sometimes lead to degraded performance over sequential iteration. 由于结果需要按顺序聚合,您将无法享受并行性的最大好处,并且有时可能导致性能降低而不是顺序迭代。 Having said that, this will do what you're after. 话虽如此,这将做你想要的。

For example, the following code will produce "[7][35][22][6][14]" consistently: 例如,以下代码将始终如一地生成"[7][35][22][6][14]"

var result = new [] { 35, 14, 22, 6, 7 }
    .AsParallel()
    .AsOrdered()
    .Select(c => "[" + c + "]")
    .Aggregate(seed: string.Empty, func: (prev, current) => prev + current);

Console.WriteLine(result);

There is a good post about PLINQ Ordering by the Parallel Programming Team. 并行编程团队有一篇关于PLINQ Ordering的好文章。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM