|
|
@ -30,17 +30,28 @@ namespace MathNet.Numerics.Threading |
|
|
{ |
|
|
{ |
|
|
using System; |
|
|
using System; |
|
|
using System.Collections.Generic; |
|
|
using System.Collections.Generic; |
|
|
using System.Threading; |
|
|
|
|
|
using Properties; |
|
|
using Properties; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Provides support for parallel loops.
|
|
|
/// Provides support for parallel loops.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
internal static class Parallel |
|
|
internal static class Parallel |
|
|
{ |
|
|
{ |
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The amount to scale the foreach buffer after each iteration.
|
|
|
|
|
|
/// </summary>
|
|
|
private const int ScalingFactor = 2; |
|
|
private const int ScalingFactor = 2; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The maximum size of the foreach buffer.
|
|
|
|
|
|
/// </summary>
|
|
|
private const int MaxBlockSize = 65536; |
|
|
private const int MaxBlockSize = 65536; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The initial size of the for each buffer.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const int IntialBlockSize = 1024; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Executes a for loop in which iterations may run in parallel.
|
|
|
/// Executes a for loop in which iterations may run in parallel.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
@ -121,29 +132,26 @@ namespace MathNet.Numerics.Threading |
|
|
/// <param name="toExclusive">The end index, exclusive.</param>
|
|
|
/// <param name="toExclusive">The end index, exclusive.</param>
|
|
|
/// <param name="localInit">The function delegate that returns the initial state of the local data for each thread.</param>
|
|
|
/// <param name="localInit">The function delegate that returns the initial state of the local data for each thread.</param>
|
|
|
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|
|
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|
|
/// <param name="localFinally">The delegate that is invoked once per iteration.</param>
|
|
|
/// <param name="localFinally">The delegate that performs a final action on the local state of each thread.</param>
|
|
|
public static void For<T>(int fromInclusive, int toExclusive, |
|
|
public static void For<T>(int fromInclusive, int toExclusive, Func<T> localInit, Func<int, T, T> body, Action<T> localFinally) |
|
|
Func<T> localInit, |
|
|
|
|
|
Func<int, T, T> body, |
|
|
|
|
|
Action<T> localFinally) |
|
|
|
|
|
{ |
|
|
{ |
|
|
var count = toExclusive - fromInclusive; |
|
|
var count = toExclusive - fromInclusive; |
|
|
var tasks = new Task<T>[ThreadQueue.ThreadCount]; |
|
|
var tasks = new Task<T>[ThreadQueue.ThreadCount]; |
|
|
var size = count / tasks.Length; |
|
|
var size = count / tasks.Length; |
|
|
|
|
|
|
|
|
if (count <= 0){ |
|
|
var intial = localInit(); |
|
|
// if (count <= 1)
|
|
|
|
|
|
// {
|
|
|
// fast forward execution if it's only one or none items
|
|
|
// if (count == 1)
|
|
|
if (count <= 1) |
|
|
// {
|
|
|
{ |
|
|
// body(fromInclusive);
|
|
|
if (count == 1) |
|
|
// }
|
|
|
{ |
|
|
|
|
|
localFinally(body(fromInclusive, intial)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var intial = localInit(); |
|
|
|
|
|
|
|
|
|
|
|
// partition the jobs into separate sets for each but the last worked thread
|
|
|
// partition the jobs into separate sets for each but the last worked thread
|
|
|
for (var i = 0; i < tasks.Length - 1; i++) |
|
|
for (var i = 0; i < tasks.Length - 1; i++) |
|
|
{ |
|
|
{ |
|
|
@ -157,8 +165,10 @@ namespace MathNet.Numerics.Threading |
|
|
{ |
|
|
{ |
|
|
localresult = body(j, (T)localresult); |
|
|
localresult = body(j, (T)localresult); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return (T)localresult; |
|
|
return (T)localresult; |
|
|
}, intial ); |
|
|
}, |
|
|
|
|
|
intial); |
|
|
ThreadQueue.Enqueue(tasks[i]); |
|
|
ThreadQueue.Enqueue(tasks[i]); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -171,20 +181,25 @@ namespace MathNet.Numerics.Threading |
|
|
{ |
|
|
{ |
|
|
localresult = body(i, (T)localresult); |
|
|
localresult = body(i, (T)localresult); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return (T)localresult; |
|
|
return (T)localresult; |
|
|
}, intial ); |
|
|
}, |
|
|
|
|
|
intial); |
|
|
|
|
|
|
|
|
ThreadQueue.Enqueue(tasks[tasks.Length - 1]); |
|
|
ThreadQueue.Enqueue(tasks[tasks.Length - 1]); |
|
|
if (tasks.Length <= 0) |
|
|
if (tasks.Length <= 0) |
|
|
{ |
|
|
{ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
WaitForTasksToComplete(tasks); |
|
|
WaitForTasksToComplete(tasks); |
|
|
|
|
|
|
|
|
for (var i = 0; i < tasks.Length; i++) |
|
|
for (var i = 0; i < tasks.Length; i++) |
|
|
{ |
|
|
{ |
|
|
localFinally(tasks[i].Result); |
|
|
localFinally(tasks[i].Result); |
|
|
} |
|
|
} |
|
|
CollectExceptionsAndDisposeTasks(tasks); |
|
|
|
|
|
|
|
|
CollectExceptions(tasks); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
@ -221,10 +236,10 @@ namespace MathNet.Numerics.Threading |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var enumerator = source.GetEnumerator(); |
|
|
var maxBlockSize = IntialBlockSize; |
|
|
var maxBlockSize = Control.InitialThreadBlockSize; |
|
|
|
|
|
var scalingFactor = ScalingFactor; |
|
|
|
|
|
var tasks = new List<Task>(); |
|
|
var tasks = new List<Task>(); |
|
|
|
|
|
|
|
|
|
|
|
var enumerator = source.GetEnumerator(); |
|
|
while (enumerator.MoveNext()) |
|
|
while (enumerator.MoveNext()) |
|
|
{ |
|
|
{ |
|
|
var pos = 0; |
|
|
var pos = 0; |
|
|
@ -249,13 +264,13 @@ namespace MathNet.Numerics.Threading |
|
|
|
|
|
|
|
|
ThreadQueue.Enqueue(task); |
|
|
ThreadQueue.Enqueue(task); |
|
|
tasks.Add(task); |
|
|
tasks.Add(task); |
|
|
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * scalingFactor); |
|
|
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * ScalingFactor); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (tasks.Count > 0) |
|
|
if (tasks.Count > 0) |
|
|
{ |
|
|
{ |
|
|
WaitForTasksToComplete(tasks.ToArray()); |
|
|
WaitForTasksToComplete(tasks.ToArray()); |
|
|
CollectExceptionsAndDisposeTasks(tasks); |
|
|
CollectExceptions(tasks); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -268,21 +283,42 @@ namespace MathNet.Numerics.Threading |
|
|
/// <param name="localInit">The function delegate that returns the initial state of the local data for each thread.</param>
|
|
|
/// <param name="localInit">The function delegate that returns the initial state of the local data for each thread.</param>
|
|
|
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|
|
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|
|
/// <param name="localFinally">The delegate that performs a final action on the local state of each thread.</param>
|
|
|
/// <param name="localFinally">The delegate that performs a final action on the local state of each thread.</param>
|
|
|
public static void ForEach<TSource, TLocal>(IEnumerable<TSource> source, Func<TLocal> localInit, |
|
|
public static void ForEach<TSource, TLocal>(IEnumerable<TSource> source, Func<TLocal> localInit, Func<TSource, TLocal, TLocal> body, Action<TLocal> localFinally) |
|
|
Func<TSource, TLocal, TLocal> body, Action<TLocal> localFinally) |
|
|
|
|
|
{ |
|
|
{ |
|
|
if (body == null) |
|
|
if (body == null) |
|
|
{ |
|
|
{ |
|
|
throw new ArgumentNullException("body"); |
|
|
throw new ArgumentNullException("body"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var enumerator = source.GetEnumerator(); |
|
|
// fast forward execution in case parallelization is disabled
|
|
|
var maxBlockSize = Control.InitialThreadBlockSize; |
|
|
if (Control.DisableParallelization |
|
|
var scalingFactor = ScalingFactor; |
|
|
|| ThreadQueue.ThreadCount <= 1 |
|
|
|
|
|
|| ThreadQueue.IsInWorkerThread) |
|
|
|
|
|
{ |
|
|
|
|
|
var localResult = localInit(); |
|
|
|
|
|
foreach (var item in source) |
|
|
|
|
|
{ |
|
|
|
|
|
localResult = body(item, localResult); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
localFinally(localResult); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// source is a IList, call For instead.
|
|
|
|
|
|
if (source is IList<TSource>) |
|
|
|
|
|
{ |
|
|
|
|
|
var list = (IList<TSource>)source; |
|
|
|
|
|
For(0, list.Count, localInit, (i, local) => body(list[i], local), localFinally); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var maxBlockSize = IntialBlockSize; |
|
|
var tasks = new List<Task<TLocal>>(); |
|
|
var tasks = new List<Task<TLocal>>(); |
|
|
|
|
|
|
|
|
var intial = localInit(); |
|
|
var intial = localInit(); |
|
|
|
|
|
|
|
|
|
|
|
var enumerator = source.GetEnumerator(); |
|
|
while (enumerator.MoveNext()) |
|
|
while (enumerator.MoveNext()) |
|
|
{ |
|
|
{ |
|
|
var pos = 0; |
|
|
var pos = 0; |
|
|
@ -304,25 +340,30 @@ namespace MathNet.Numerics.Threading |
|
|
{ |
|
|
{ |
|
|
localresult = body(list[i], (TLocal)localresult); |
|
|
localresult = body(list[i], (TLocal)localresult); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return (TLocal)localresult; |
|
|
return (TLocal)localresult; |
|
|
}, intial); |
|
|
}, |
|
|
|
|
|
intial); |
|
|
|
|
|
|
|
|
ThreadQueue.Enqueue(task); |
|
|
ThreadQueue.Enqueue(task); |
|
|
tasks.Add(task); |
|
|
tasks.Add(task); |
|
|
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * scalingFactor); |
|
|
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * ScalingFactor); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (tasks.Count <= 0) |
|
|
if (tasks.Count <= 0) |
|
|
{ |
|
|
{ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var taskArray = tasks.ToArray(); |
|
|
var taskArray = tasks.ToArray(); |
|
|
WaitForTasksToComplete(taskArray); |
|
|
WaitForTasksToComplete(taskArray); |
|
|
|
|
|
|
|
|
for (var i = 0; i < taskArray.Length; i++) |
|
|
for (var i = 0; i < taskArray.Length; i++) |
|
|
{ |
|
|
{ |
|
|
localFinally(tasks[i].Result); |
|
|
localFinally(tasks[i].Result); |
|
|
} |
|
|
} |
|
|
CollectExceptionsAndDisposeTasks(taskArray); |
|
|
|
|
|
|
|
|
CollectExceptions(taskArray); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
@ -355,7 +396,7 @@ namespace MathNet.Numerics.Threading |
|
|
|| ThreadQueue.ThreadCount <= 1 |
|
|
|| ThreadQueue.ThreadCount <= 1 |
|
|
|| ThreadQueue.IsInWorkerThread) |
|
|
|| ThreadQueue.IsInWorkerThread) |
|
|
{ |
|
|
{ |
|
|
for (int i = 0; i < actions.Length; i++) |
|
|
for (var i = 0; i < actions.Length; i++) |
|
|
{ |
|
|
{ |
|
|
actions[i](); |
|
|
actions[i](); |
|
|
} |
|
|
} |
|
|
@ -392,7 +433,7 @@ namespace MathNet.Numerics.Threading |
|
|
|
|
|
|
|
|
WaitForTasksToComplete(tasks); |
|
|
WaitForTasksToComplete(tasks); |
|
|
|
|
|
|
|
|
CollectExceptionsAndDisposeTasks(tasks); |
|
|
CollectExceptions(tasks); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
@ -401,17 +442,17 @@ namespace MathNet.Numerics.Threading |
|
|
/// <param name="tasks">The tasks.</param>
|
|
|
/// <param name="tasks">The tasks.</param>
|
|
|
private static void WaitForTasksToComplete(Task[] tasks) |
|
|
private static void WaitForTasksToComplete(Task[] tasks) |
|
|
{ |
|
|
{ |
|
|
for (var i = 0; i < tasks.Length; i++) |
|
|
for (var i = 0; i < tasks.Length; i++) |
|
|
{ |
|
|
{ |
|
|
tasks[i].Wait(); |
|
|
tasks[i].Wait(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Collects the exceptions and dispose tasks.
|
|
|
/// Collects the exceptions and dispose tasks.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="tasks">The tasks.</param>
|
|
|
/// <param name="tasks">The tasks.</param>
|
|
|
private static void CollectExceptionsAndDisposeTasks(IEnumerable<Task> tasks) |
|
|
private static void CollectExceptions(IEnumerable<Task> tasks) |
|
|
{ |
|
|
{ |
|
|
// collect all thrown exceptions and dispose the jobs
|
|
|
// collect all thrown exceptions and dispose the jobs
|
|
|
var exceptions = new List<Exception>(); |
|
|
var exceptions = new List<Exception>(); |
|
|
|