2015-08-26 3 views
4

.NET Framework позволяет легко делать параллельную агрегацию, но в соответствии с the documentation, она работает только для коммутативных операций, то есть операция, при которой е (х, у) = е (у , х):Есть ли простой способ сделать параллельную агрегацию с некоммутативной операцией?

реализация .NET из параллельного шаблона Aggregation также ожидает операции коммутативным.

Я хочу объединить строковые значения с помощью конкатенации, то есть некоммутативной операции. Последовательный подход выглядит следующим образом:

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

так что если this.LongOperation возвращает последовательно Hello, World и !, конечный результат HelloWorld!.

Если я использую параллельную агрегацию, результат может быть HelloWorld, но и World!Hello, !HelloWorld и т.д.

Обходной бы сделать что-то похожее на:

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); 

с (неважно, в моем конкретном случае) недостаток, что вся последовательность будет оцениваться на этапе OrderBy в любом случае, не дожидаясь агрегации. Другой способ написать это:

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 Я ожидал, чтобы сделать это, или есть более простой способ сделать вещь?

ответ

1

Вы ищете ParallelEnumerable.AsOrdered:

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

Тот факт, что вам нужно, чтобы сохранить порядок будет иметь падение производительности в вашем запросе. Поскольку результаты необходимо агрегировать по порядку, вы не будете пользоваться максимальной выгодой от параллелизма и иногда могут приводить к ухудшению производительности при последовательной итерации. Сказав это, это сделает то, что вам нужно.

Например, следующий код будет производить "[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); 

Существует хороший пост о PLINQ Ordering по параллельному программированию команды.

+0

@downvoter - Поучительно объяснять? –

+0

Этот ответ совершенно неверен. Агрегат - это метод для Enumerable. Рассмотрим случай, когда тип аккумулятора отличается от типа элементов. Вам понадобится алгоритм сокращения карты. – Aron

+0

@Aron - Это именно то, что он делает с «Агрегатом», Он делает сокращение. Он также хочет сохранить порядок, согласно указанному вопросу. Это именно то, что он делает в своем примере. i –

Смежные вопросы