Reputation: 4305
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
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
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
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