18 changed files with 1534 additions and 507 deletions
File diff suppressed because it is too large
@ -0,0 +1,147 @@ |
|||||
|
// <copyright file="CommonParallel.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
// Copyright (c) 2009-2010 Math.NET
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
#if !SILVERLIGHT
|
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Threading.Tasks; |
||||
|
#endif
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Used to simplify parallel code, particularly between the .NET 4.0 and Silverlight Code.
|
||||
|
/// </summary>
|
||||
|
internal static class CommonParallel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Executes a for loop in which iterations may run in parallel.
|
||||
|
/// </summary>
|
||||
|
/// <param name="fromInclusive">The start index, inclusive.</param>
|
||||
|
/// <param name="toExclusive">The end index, exclusive.</param>
|
||||
|
/// <param name="body">The body to be invoked for each iteration.</param>
|
||||
|
/// <exception cref="ArgumentNullException">The <paramref name="body"/> argument is null.</exception>
|
||||
|
/// <exception cref="AggregateException">At least one invocation of the body threw an exception.</exception>
|
||||
|
public static void For(int fromInclusive, int toExclusive, Action<int> body) |
||||
|
{ |
||||
|
#if SILVERLIGHT
|
||||
|
Parallel.For(fromInclusive, toExclusive, body); |
||||
|
#else
|
||||
|
if (Control.DisableParallelization || Control.NumberOfParallelWorkerThreads < 2) |
||||
|
{ |
||||
|
for (var index = fromInclusive; index < toExclusive; index++) |
||||
|
{ |
||||
|
body(index); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Parallel.ForEach( |
||||
|
Partitioner.Create(fromInclusive, toExclusive), |
||||
|
new ParallelOptions { MaxDegreeOfParallelism = Control.NumberOfParallelWorkerThreads }, |
||||
|
(range, loopState) => |
||||
|
{ |
||||
|
for (var i = range.Item1; i < range.Item2; i++) |
||||
|
{ |
||||
|
body(i); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Aggregates a function over a loop.
|
||||
|
/// </summary>
|
||||
|
/// <param name="fromInclusive">Starting index of the loop.</param>
|
||||
|
/// <param name="toExclusive">Ending index of the loop</param>
|
||||
|
/// <param name="body">The function to aggregate.</param>
|
||||
|
/// <returns>The sum of the function over the loop.</returns>
|
||||
|
public static double Aggregate(int fromInclusive, int toExclusive, Func<int, double> body) |
||||
|
{ |
||||
|
var sync = new object(); |
||||
|
var sum = 0.0; |
||||
|
|
||||
|
#if SILVERLIGHT
|
||||
|
Parallel.For( |
||||
|
fromInclusive, |
||||
|
toExclusive, |
||||
|
() => 0.0, |
||||
|
(i, localData) => localData += body(i), |
||||
|
localResult => |
||||
|
{ |
||||
|
lock (sync) |
||||
|
{ |
||||
|
sum += localResult; |
||||
|
} |
||||
|
}); |
||||
|
#else
|
||||
|
|
||||
|
if (Control.DisableParallelization || Control.NumberOfParallelWorkerThreads < 2) |
||||
|
{ |
||||
|
for (var index = fromInclusive; index < toExclusive; index++) |
||||
|
{ |
||||
|
sum += body(index); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Parallel.ForEach( |
||||
|
Partitioner.Create(fromInclusive, toExclusive), |
||||
|
new ParallelOptions { MaxDegreeOfParallelism = Control.NumberOfParallelWorkerThreads }, |
||||
|
() => 0.0, |
||||
|
(range, loopState, localData) => |
||||
|
{ |
||||
|
for (var i = range.Item1; i < range.Item2; i++) |
||||
|
{ |
||||
|
localData += body(i); |
||||
|
} |
||||
|
|
||||
|
return localData; |
||||
|
}, |
||||
|
localResult => |
||||
|
{ |
||||
|
lock (sync) |
||||
|
{ |
||||
|
sum += localResult; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
#endif
|
||||
|
return sum; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes each of the provided actions inside a discrete, asynchronous task.
|
||||
|
/// </summary>
|
||||
|
/// <param name="actions">An array of actions to execute.</param>
|
||||
|
/// <exception cref="ArgumentException">The actions array contains a null element.</exception>
|
||||
|
/// <exception cref="AggregateException">An action threw an exception.</exception>
|
||||
|
public static void Invoke(params Action[] actions) |
||||
|
{ |
||||
|
Parallel.Invoke(actions); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
// <copyright file="AggregateException.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.ObjectModel; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents multiple errors that occur during application execution.
|
||||
|
/// </summary>
|
||||
|
public class AggregateException : Exception |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// List of the aggregated exceptions.
|
||||
|
/// </summary>
|
||||
|
private readonly IList<Exception> _exceptions = new List<Exception>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the AggregateException class with a specified error message and references to the inner exceptions that are the cause of this exception.
|
||||
|
/// </summary>
|
||||
|
/// <param name="exceptions">The exceptions that are the cause of the current exception.</param>
|
||||
|
public AggregateException(IEnumerable<Exception> exceptions) |
||||
|
{ |
||||
|
foreach (var exception in exceptions) |
||||
|
{ |
||||
|
this._exceptions.Add(exception); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a read-only collection of the Exception instances that caused the current exception.
|
||||
|
/// </summary>
|
||||
|
/// <value>A read-only collection of the Exception instances that caused the current exception</value>
|
||||
|
public ReadOnlyCollection<Exception> InnerExceptions |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return new ReadOnlyCollection<Exception>(this._exceptions); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,485 @@ |
|||||
|
// <copyright file="Parallel.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Properties; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides support for parallel loops.
|
||||
|
/// </summary>
|
||||
|
internal static class Parallel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The amount to scale the foreach buffer after each iteration.
|
||||
|
/// </summary>
|
||||
|
private const int ScalingFactor = 2; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The maximum size of the foreach buffer.
|
||||
|
/// </summary>
|
||||
|
private const int MaxBlockSize = 65536; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The initial size of the for each buffer.
|
||||
|
/// </summary>
|
||||
|
private const int IntialBlockSize = 1024; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes a for loop in which iterations may run in parallel.
|
||||
|
/// </summary>
|
||||
|
/// <param name="fromInclusive">The start index, inclusive.</param>
|
||||
|
/// <param name="toExclusive">The end index, exclusive.</param>
|
||||
|
/// <param name="body">The body to be invoked for each iteration.</param>
|
||||
|
/// <exception cref="ArgumentNullException">The <paramref name="body"/> argument is null.</exception>
|
||||
|
/// <exception cref="AggregateException">At least one invocation of the body threw an exception.</exception>
|
||||
|
public static void For(int fromInclusive, int toExclusive, Action<int> body) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
// fast forward execution if it's only one or none items
|
||||
|
var count = toExclusive - fromInclusive; |
||||
|
if (count <= 1) |
||||
|
{ |
||||
|
if (count == 1) |
||||
|
{ |
||||
|
body(fromInclusive); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// fast forward execution in case parallelization is disabled
|
||||
|
if (Control.DisableParallelization |
||||
|
|| ThreadQueue.ThreadCount <= 1 |
||||
|
|| ThreadQueue.IsInWorkerThread) |
||||
|
{ |
||||
|
for (int i = fromInclusive; i < toExclusive; i++) |
||||
|
{ |
||||
|
body(i); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var actions = new Action[ThreadQueue.ThreadCount]; |
||||
|
var size = count / actions.Length; |
||||
|
|
||||
|
// partition the jobs into separate sets for each but the last worked thread
|
||||
|
for (var i = 0; i < actions.Length - 1; i++) |
||||
|
{ |
||||
|
var start = fromInclusive + (i * size); |
||||
|
var stop = fromInclusive + ((i + 1) * size); |
||||
|
|
||||
|
actions[i] = |
||||
|
() => |
||||
|
{ |
||||
|
for (int j = start; j < stop; j++) |
||||
|
{ |
||||
|
body(j); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// add another set for last worker thread
|
||||
|
actions[actions.Length - 1] = |
||||
|
() => |
||||
|
{ |
||||
|
for (int i = fromInclusive + ((actions.Length - 1) * size); i < toExclusive; i++) |
||||
|
{ |
||||
|
body(i); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Invoke(actions); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes a for loop in which iterations may run in parallel.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of the thread-local data.</typeparam>
|
||||
|
/// <param name="fromInclusive">The start index, inclusive.</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="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>
|
||||
|
public static void For<T>(int fromInclusive, int toExclusive, Func<T> localInit, Func<int, T, T> body, Action<T> localFinally) |
||||
|
{ |
||||
|
var count = toExclusive - fromInclusive; |
||||
|
var tasks = new Task<T>[ThreadQueue.ThreadCount]; |
||||
|
var size = count / tasks.Length; |
||||
|
|
||||
|
// fast forward execution if it's only one or none items
|
||||
|
if (count <= 1) |
||||
|
{ |
||||
|
if (count == 1) |
||||
|
{ |
||||
|
localFinally(body(fromInclusive, localInit())); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// fast forward execution in case parallelization is disabled
|
||||
|
if (Control.DisableParallelization |
||||
|
|| ThreadQueue.ThreadCount <= 1 |
||||
|
|| ThreadQueue.IsInWorkerThread) |
||||
|
{ |
||||
|
var localresult = localInit(); |
||||
|
for (var i = fromInclusive; i < toExclusive; i++) |
||||
|
{ |
||||
|
localresult = body(i, localresult); |
||||
|
} |
||||
|
|
||||
|
localFinally(localresult); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// partition the jobs into separate sets for each but the last worked thread
|
||||
|
for (var i = 0; i < tasks.Length - 1; i++) |
||||
|
{ |
||||
|
var start = fromInclusive + (i * size); |
||||
|
var stop = fromInclusive + ((i + 1) * size); |
||||
|
tasks[i] = new Task<T>( |
||||
|
localData => |
||||
|
{ |
||||
|
var localresult = (T)localData; |
||||
|
for (var j = start; j < stop; j++) |
||||
|
{ |
||||
|
localresult = body(j, localresult); |
||||
|
} |
||||
|
|
||||
|
return localresult; |
||||
|
}, |
||||
|
localInit()); |
||||
|
ThreadQueue.Enqueue(tasks[i]); |
||||
|
} |
||||
|
|
||||
|
// add another set for last worker thread
|
||||
|
tasks[tasks.Length - 1] = new Task<T>( |
||||
|
localData => |
||||
|
{ |
||||
|
var localresult = (T)localData; |
||||
|
for (var i = fromInclusive + ((tasks.Length - 1) * size); i < toExclusive; i++) |
||||
|
{ |
||||
|
localresult = body(i, localresult); |
||||
|
} |
||||
|
|
||||
|
return localresult; |
||||
|
}, |
||||
|
localInit()); |
||||
|
|
||||
|
ThreadQueue.Enqueue(tasks[tasks.Length - 1]); |
||||
|
if (tasks.Length <= 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
WaitForTasksToComplete(tasks); |
||||
|
|
||||
|
foreach (var t in tasks) |
||||
|
{ |
||||
|
localFinally(t.Result); |
||||
|
} |
||||
|
|
||||
|
CollectExceptions(tasks); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes a for each operation on an IEnumerable{T} in which iterations may run in parallel.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of the data in the source.</typeparam>
|
||||
|
/// <param name="source">An enumerable data source.</param>
|
||||
|
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
||||
|
public static void ForEach<T>(IEnumerable<T> source, Action<T> body) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
// fast forward execution in case parallelization is disabled
|
||||
|
if (Control.DisableParallelization |
||||
|
|| ThreadQueue.ThreadCount <= 1 |
||||
|
|| ThreadQueue.IsInWorkerThread) |
||||
|
{ |
||||
|
foreach (var item in source) |
||||
|
{ |
||||
|
body(item); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// source is a IList, call For instead.
|
||||
|
if (source is IList<T>) |
||||
|
{ |
||||
|
var list = (IList<T>)source; |
||||
|
For(0, list.Count, i => body(list[i])); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var maxBlockSize = IntialBlockSize; |
||||
|
var tasks = new List<Task>(); |
||||
|
|
||||
|
var enumerator = source.GetEnumerator(); |
||||
|
while (enumerator.MoveNext()) |
||||
|
{ |
||||
|
var pos = 0; |
||||
|
var list = new T[maxBlockSize]; |
||||
|
list[pos++] = enumerator.Current; |
||||
|
|
||||
|
var count = 1; |
||||
|
while (count < maxBlockSize && enumerator.MoveNext()) |
||||
|
{ |
||||
|
list[pos++] = enumerator.Current; |
||||
|
count++; |
||||
|
} |
||||
|
|
||||
|
var task = new Task( |
||||
|
() => |
||||
|
{ |
||||
|
for (var i = 0; i < pos; i++) |
||||
|
{ |
||||
|
body(list[i]); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
ThreadQueue.Enqueue(task); |
||||
|
tasks.Add(task); |
||||
|
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * ScalingFactor); |
||||
|
} |
||||
|
|
||||
|
if (tasks.Count > 0) |
||||
|
{ |
||||
|
WaitForTasksToComplete(tasks.ToArray()); |
||||
|
CollectExceptions(tasks); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes a for each operation on an IEnumerable{TSource in which iterations may run in parallel.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSource">The type of the data in the source.</typeparam>
|
||||
|
/// <typeparam name="TLocal">The type of the thread-local data.</typeparam>
|
||||
|
/// <param name="source">An enumerable data source.</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="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, Func<TSource, TLocal, TLocal> body, Action<TLocal> localFinally) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
// fast forward execution in case parallelization is disabled
|
||||
|
if (Control.DisableParallelization |
||||
|
|| 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 enumerator = source.GetEnumerator(); |
||||
|
while (enumerator.MoveNext()) |
||||
|
{ |
||||
|
var pos = 0; |
||||
|
var list = new TSource[maxBlockSize]; |
||||
|
list[pos++] = enumerator.Current; |
||||
|
|
||||
|
var count = 1; |
||||
|
while (count < maxBlockSize && enumerator.MoveNext()) |
||||
|
{ |
||||
|
list[pos++] = enumerator.Current; |
||||
|
count++; |
||||
|
} |
||||
|
|
||||
|
var task = new Task<TLocal>( |
||||
|
localData => |
||||
|
{ |
||||
|
var localresult = localData; |
||||
|
for (var i = 0; i < pos; i++) |
||||
|
{ |
||||
|
localresult = body(list[i], (TLocal)localresult); |
||||
|
} |
||||
|
|
||||
|
return (TLocal)localresult; |
||||
|
}, |
||||
|
localInit()); |
||||
|
|
||||
|
ThreadQueue.Enqueue(task); |
||||
|
tasks.Add(task); |
||||
|
maxBlockSize = Math.Min(MaxBlockSize, maxBlockSize * ScalingFactor); |
||||
|
} |
||||
|
|
||||
|
if (tasks.Count <= 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var taskArray = tasks.ToArray(); |
||||
|
WaitForTasksToComplete(taskArray); |
||||
|
|
||||
|
for (var i = 0; i < taskArray.Length; i++) |
||||
|
{ |
||||
|
localFinally(tasks[i].Result); |
||||
|
} |
||||
|
|
||||
|
CollectExceptions(taskArray); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes each of the provided actions inside a discrete, asynchronous task.
|
||||
|
/// </summary>
|
||||
|
/// <param name="actions">An array of actions to execute.</param>
|
||||
|
/// <exception cref="ArgumentNullException">The <paramref name="actions"/> argument is null.</exception>
|
||||
|
/// <exception cref="ArgumentException">The actions array contains a null element.</exception>
|
||||
|
/// <exception cref="AggregateException">An action threw an exception.</exception>
|
||||
|
public static void Run(params Action[] actions) |
||||
|
{ |
||||
|
if (actions == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("actions"); |
||||
|
} |
||||
|
|
||||
|
// fast forward execution if it's only one or none items
|
||||
|
if (actions.Length <= 1) |
||||
|
{ |
||||
|
if (actions.Length == 1) |
||||
|
{ |
||||
|
actions[0](); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// fast forward execution in case parallelization is disabled
|
||||
|
if (Control.DisableParallelization |
||||
|
|| ThreadQueue.ThreadCount <= 1 |
||||
|
|| ThreadQueue.IsInWorkerThread) |
||||
|
{ |
||||
|
for (var i = 0; i < actions.Length; i++) |
||||
|
{ |
||||
|
actions[i](); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Invoke(actions); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes each of the provided actions inside a discrete, asynchronous task.
|
||||
|
/// </summary>
|
||||
|
/// <param name="actions">An array of actions to execute.</param>
|
||||
|
/// <exception cref="ArgumentException">The actions array contains a null element.</exception>
|
||||
|
/// <exception cref="AggregateException">An action threw an exception.</exception>
|
||||
|
internal static void Invoke(params Action[] actions) |
||||
|
{ |
||||
|
// create a job for each action
|
||||
|
var tasks = new Task[actions.Length]; |
||||
|
for (var i = 0; i < tasks.Length; i++) |
||||
|
{ |
||||
|
Action action = actions[i]; |
||||
|
if (action == null) |
||||
|
{ |
||||
|
throw new ArgumentException(String.Format(Resources.ArgumentItemNull, "actions"), "actions"); |
||||
|
} |
||||
|
|
||||
|
tasks[i] = new Task(action); |
||||
|
} |
||||
|
|
||||
|
// run the jobs
|
||||
|
ThreadQueue.Enqueue(tasks); |
||||
|
|
||||
|
WaitForTasksToComplete(tasks); |
||||
|
|
||||
|
CollectExceptions(tasks); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Waits for tasks to complete.
|
||||
|
/// </summary>
|
||||
|
/// <param name="tasks">The tasks.</param>
|
||||
|
private static void WaitForTasksToComplete(Task[] tasks) |
||||
|
{ |
||||
|
foreach (var task in tasks) |
||||
|
{ |
||||
|
task.Wait(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Collects the exceptions and dispose tasks.
|
||||
|
/// </summary>
|
||||
|
/// <param name="tasks">The tasks.</param>
|
||||
|
private static void CollectExceptions(IEnumerable<Task> tasks) |
||||
|
{ |
||||
|
// collect all thrown exceptions and dispose the jobs
|
||||
|
var exceptions = new List<Exception>(); |
||||
|
foreach (var task in tasks) |
||||
|
{ |
||||
|
if (task.IsFaulted) |
||||
|
{ |
||||
|
exceptions.Add(task.Exception); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// throw the aggregated exceptions, if any
|
||||
|
if (exceptions.Count > 0) |
||||
|
{ |
||||
|
throw new AggregateException(exceptions); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
// <copyright file="Task.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Internal Parallel Task Handle.
|
||||
|
/// </summary>
|
||||
|
internal class Task |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Delegate to the task's action.
|
||||
|
/// </summary>
|
||||
|
private readonly System.Action _body; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the Task class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="body">Delegate to the task's action.</param>
|
||||
|
public Task(Action body) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
_body = body; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Task"/> class.
|
||||
|
/// </summary>
|
||||
|
protected Task() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether the task completed due to an unhandled exception.
|
||||
|
/// </summary>
|
||||
|
/// <value>
|
||||
|
/// <c>true</c> if this task completed due to an unhandled exception; otherwise, <c>false</c>.
|
||||
|
/// </value>
|
||||
|
public bool IsFaulted |
||||
|
{ |
||||
|
get { return Exception != null; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this task has completed.
|
||||
|
/// </summary>
|
||||
|
/// <value>
|
||||
|
/// <c>true</c> if this task has completed; otherwise, <c>false</c>.
|
||||
|
/// </value>
|
||||
|
public bool IsCompleted |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the exception thrown by the task, if any.
|
||||
|
/// </summary>
|
||||
|
public Exception Exception { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Run the task.
|
||||
|
/// </summary>
|
||||
|
public void Compute() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
DoCompute(); |
||||
|
IsCompleted = true; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Exception = e; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Runs the actual task.
|
||||
|
/// </summary>
|
||||
|
protected virtual void DoCompute() |
||||
|
{ |
||||
|
_body(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Waits for the task to complete execution.
|
||||
|
/// </summary>
|
||||
|
public void Wait() |
||||
|
{ |
||||
|
while (!IsCompleted && !IsFaulted) |
||||
|
{ |
||||
|
Thread.Sleep(0); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
// <copyright file="TaskOfT.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Internal Generic Parallel Task Handle.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TResult">The type of the result.</typeparam>
|
||||
|
internal class Task<TResult> : Task |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Delegate to the task's action.
|
||||
|
/// </summary>
|
||||
|
private readonly Func<object, TResult> _body; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Variable used to hold state information between iterations.
|
||||
|
/// </summary>
|
||||
|
private readonly object _state; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the result of the task.
|
||||
|
/// </summary>
|
||||
|
/// <value>The result of the task.</value>
|
||||
|
public TResult Result { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the Task class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="body">Delegate to the task's action.</param>
|
||||
|
/// <param name="state">An object representing data to be used by the action.</param>
|
||||
|
public Task(Func<object, TResult> body, object state) |
||||
|
{ |
||||
|
if (body == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("body"); |
||||
|
} |
||||
|
|
||||
|
_state = state; |
||||
|
_body = body; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Runs the actual task.
|
||||
|
/// </summary>
|
||||
|
protected override void DoCompute() |
||||
|
{ |
||||
|
Result = _body(_state); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,258 @@ |
|||||
|
// <copyright file="ThreadQueue.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Internal Parallel Thread Queue.
|
||||
|
/// </summary>
|
||||
|
internal static class ThreadQueue |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Sync Object for the thread queue state.
|
||||
|
/// </summary>
|
||||
|
private static readonly object _stateSync = new object(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sync Object for queue access (to be sure it's used by us only).
|
||||
|
/// </summary>
|
||||
|
private static readonly object _queueSync = new object(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Queue holding the pending jobs.
|
||||
|
/// </summary>
|
||||
|
private static readonly Queue<Task> _queue = new Queue<Task>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Running flag, used to signal worker threads to stop cleanly.
|
||||
|
/// </summary>
|
||||
|
private static bool _running = true; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Worker threads
|
||||
|
/// </summary>
|
||||
|
private static Thread[] _threads; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the number of worker threads.
|
||||
|
/// </summary>
|
||||
|
internal static int ThreadCount { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Indicating whether the current thread is a parallelized worker thread.
|
||||
|
/// </summary>
|
||||
|
[ThreadStatic] |
||||
|
private static bool _isInWorkerThread; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes static members of the ThreadQueue class.
|
||||
|
/// </summary>
|
||||
|
static ThreadQueue() |
||||
|
{ |
||||
|
Start(1); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether the current thread is a parallelized worker thread.
|
||||
|
/// </summary>
|
||||
|
public static bool IsInWorkerThread |
||||
|
{ |
||||
|
get { return _isInWorkerThread; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Add a job to the queue.
|
||||
|
/// </summary>
|
||||
|
/// <param name="task">The job to run.</param>
|
||||
|
public static void Enqueue(Task task) |
||||
|
{ |
||||
|
if (!_running) |
||||
|
{ |
||||
|
Start(); |
||||
|
} |
||||
|
|
||||
|
lock (_queueSync) |
||||
|
{ |
||||
|
_queue.Enqueue(task); |
||||
|
Monitor.Pulse(_queueSync); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Add a set of jobs to the queue.
|
||||
|
/// </summary>
|
||||
|
/// <param name="tasks">The jobs to run.</param>
|
||||
|
public static void Enqueue(IList<Task> tasks) |
||||
|
{ |
||||
|
if (!_running) |
||||
|
{ |
||||
|
Start(); |
||||
|
} |
||||
|
|
||||
|
lock (_queueSync) |
||||
|
{ |
||||
|
foreach (var task in tasks) |
||||
|
{ |
||||
|
_queue.Enqueue(task); |
||||
|
} |
||||
|
|
||||
|
Monitor.PulseAll(_queueSync); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Worker Thread Program
|
||||
|
/// </summary>
|
||||
|
private static void WorkerThreadStart() |
||||
|
{ |
||||
|
_isInWorkerThread = true; |
||||
|
|
||||
|
while (_running) |
||||
|
{ |
||||
|
// Get the job...
|
||||
|
Task task = null; |
||||
|
|
||||
|
lock (_queueSync) |
||||
|
{ |
||||
|
// Check whether we should shut down
|
||||
|
if (!_running) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (_queue.Count > 0) |
||||
|
{ |
||||
|
task = _queue.Dequeue(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Monitor.Wait(_queueSync); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (task == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// ...and run it
|
||||
|
task.Compute(); |
||||
|
} |
||||
|
|
||||
|
_isInWorkerThread = false; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Start or restart the queue with the specified number of worker threads.
|
||||
|
/// </summary>
|
||||
|
/// <param name="numberOfThreads">Number of worker threads.</param>
|
||||
|
public static void Start(int numberOfThreads) |
||||
|
{ |
||||
|
lock (_stateSync) |
||||
|
{ |
||||
|
// instead of throwing an out of range exception, simply normalize
|
||||
|
numberOfThreads = Math.Max(1, Math.Min(1024, numberOfThreads)); |
||||
|
|
||||
|
if (_threads != null) |
||||
|
{ |
||||
|
if (_threads.Length == numberOfThreads) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Shutdown(); |
||||
|
} |
||||
|
|
||||
|
ThreadCount = numberOfThreads; |
||||
|
Start(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Start the thread queue, if it is not already running.
|
||||
|
/// </summary>
|
||||
|
public static void Start() |
||||
|
{ |
||||
|
lock (_stateSync) |
||||
|
{ |
||||
|
if (_threads != null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_running = true; |
||||
|
|
||||
|
_threads = new Thread[ThreadCount]; |
||||
|
|
||||
|
for (var i = 0; i < _threads.Length; i++) |
||||
|
{ |
||||
|
_threads[i] = new Thread(WorkerThreadStart) |
||||
|
{ |
||||
|
IsBackground = true |
||||
|
}; |
||||
|
|
||||
|
_threads[i].Start(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Stop the thread queue, if it is running.
|
||||
|
/// </summary>
|
||||
|
public static void Shutdown() |
||||
|
{ |
||||
|
// try to stop the worker threads cleanly
|
||||
|
lock (_stateSync) |
||||
|
{ |
||||
|
if (_threads == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_running = false; |
||||
|
|
||||
|
lock (_queueSync) |
||||
|
{ |
||||
|
Monitor.PulseAll(_queueSync); |
||||
|
} |
||||
|
|
||||
|
// wait until all threads have stopped
|
||||
|
foreach (var thread in _threads) |
||||
|
{ |
||||
|
thread.Join(); |
||||
|
} |
||||
|
|
||||
|
_threads = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue