Matthew Crews
Matthew Crews

Reputation: 4305

TPL Parallel.For with long running tasks

I am wanting to use the Task Parallel Library (TPL) in F# to execute many (>1000) long running tasks. Here is my current code:

Parallel.For(1, numberOfSets, fun j ->
    //Long running task here
    )

When I start this it appears that .NET initiates all of the tasks at once and bounces between them constantly. What would be better is if it stayed on a task until it is done before moving to the next one. This would minimize the context switching.

Is there a way to provide a hint to the scheduler? I know that it is possible to provide hints but I cannot find clear examples or is the scheduler already smart about this and it's just my perception that there are too many context switches occuring. Thanks for the help!

Upvotes: 7

Views: 1534

Answers (3)

pad
pad

Reputation: 41290

From my experience, for a big number of tasks it's better to bound MaxDegreeOfParallelism linearly to Environment.ProcessorCount.

Here is a similar code fragment to @Mimo's one in F# syntax:

let options = ParallelOptions()
options.MaxDegreeOfParallelism <- Environment.ProcessorCount * 2

Parallel.For(0, n, options, 
             (fun i -> (* Long running task here *))) |> ignore

Since you're working with parallel programming in F#, please take a look at the excellent book "Parallel Programming with Microsoft .NET", particularly the chapter on "Parallel Loops". @Tomas has translated its samples to F# and they're available here.

Upvotes: 5

Ohad Schneider
Ohad Schneider

Reputation: 38132

Looking at the reference source, it appears the following piece of code determines the number of workers:

// initialize ranges with passed in loop arguments and expected number of workers 
int numExpectedWorkers = (parallelOptions.EffectiveMaxConcurrencyLevel == -1) ?
    Environment.ProcessorCount : 
    parallelOptions.EffectiveMaxConcurrencyLevel; 

As far as I can tell, with the default task scheduler and default ParallelOptions this evaluates to Environment.ProcessorCount, so it's weird that you're getting a different behavior by specifying MaxDegreeOfParallelism to the processor count yourself. I suggest you debug to make sure there really is a difference (you could print the Thread.ManagedThreadId inside the long running task).

Upvotes: 1

MiMo
MiMo

Reputation: 11973

We had a similar problem - using C# instead than F#, but the libraries are the same. The solution was to limit the degree of parallelism:

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 16;
Parallel.For(0, n, parallelOptions, i => {
   . . . 
});

16 worked well for our tasks - you should experiment to see which value is better in your case.

Upvotes: 8

Related Questions