Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ordered PLINQ ForAll

Tags:

c#

plinq

The msdn documentation about order preservation in PLINQ states the following about ForAll().

  • Result when the source sequence is ordered: Executes nondeterministically in parallel
  • Result when the source sequence is unordered: Executes nondeterministically in parallel

Does this mean that ordered execution of the ForAll method is never guaranteed?

I haven't used PLINQ before, but the following Code Review question seemed like an appropriate usage for it. At the bottom of my answer I write:

Events.AsParallel().AsOrdered().ForAll( eventItem =>
{
    ...
} );    

After reading the documentation I believe the AsOrdered() wouldn't change anything?
I'm also suspecting the previous query can't replace a simple for loop where order is important?
Probably parallel calls to the StringBuilder will also occur, resulting in a wrong output?

like image 787
Steven Jeuris Avatar asked Mar 18 '11 13:03

Steven Jeuris


2 Answers

Now as an extension method:

It will process on multiple cores then will order the results, so there's the overhead of ordering. Here's an answer on benchmarking simple for vs parallel.

 public static IEnumerable<T1> OrderedParallel<T, T1>(this IEnumerable<T> list, Func<T, T1> action)
    {
        var unorderedResult = new ConcurrentBag<(long, T1)>();
        Parallel.ForEach(list, (o, state, i) =>
        {
            unorderedResult.Add((i, action.Invoke(o)));
        });
        var ordered = unorderedResult.OrderBy(o => o.Item1);
        return ordered.Select(o => o.Item2);
    }

use like:

var result = Events.OrderedParallel(eventItem => ...);

Hope this will save you some time.

like image 111
Matas Vaitkevicius Avatar answered Oct 11 '22 11:10

Matas Vaitkevicius


As others have rightly answered, the ForAll method is never guaranteed to execute an action for enumerable elements in any particular order, and will ignore the AsOrdered() method call silently.

For the benefit of readers having a valid reason to execute an action for enumerable elements in a way that stay's as close to the original order (as far as is reasonable in a parallel processing context) the extension methods below might help.

public static void ForAllInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action) {

    Partitioner.Create( source )
               .AsParallel()
               .AsOrdered()
               .ForAll( e => action( e ) );

}

This can then be used as follows:

orderedElements.AsParallel()
               .ForAllInApproximateOrder( e => DoSomething( e ) );

It should be noted that the above extension method uses PLINQ ForAll and not Parallel.ForEach and so inherits the threading model used interally by PLINQ (which is different to that used by Parallel.ForEach -- less aggressive by default in my experience). A similar extension method using Parallel.ForEach is below.

public static void ForEachInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action) {

    source = Partitioner.Create( source )
                        .AsParallel()
                        .AsOrdered();

    Parallel.ForEach( source , e => action( e ) );

}

This can then be used as follows:

orderedElements.AsParallel()
               .ForEachInApproximateOrder( e => DoSomething( e ) );

There is no need to chain AsOrdered() to your query when using either of the above extension methods, it gets called internally anyway.

I have found these methods useful in processing elements that have coarse grained significance. It can be useful, for example, to process records starting at the oldest and working towards the newest. In many cases the exact order of records isn't required - so far as older records generally get processed before newer records. Similarly, records having low/med/high priority levels can be processed such that high priority records will be processed before lower priority records for the majority of cases, with the edge cases not far behind.

like image 42
FantasticJamieBurns Avatar answered Oct 11 '22 11:10

FantasticJamieBurns