forked from tsai/mathnet-numerics
23 changed files with 1471 additions and 3075 deletions
File diff suppressed because it is too large
@ -1,69 +0,0 @@ |
|||||
// <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>
|
|
||||
#if !SILVERLIGHT
|
|
||||
[Serializable] |
|
||||
#endif
|
|
||||
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) |
|
||||
{ |
|
||||
_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>(_exceptions); } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,485 +0,0 @@ |
|||||
// <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>
|
|
||||
private 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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,127 +0,0 @@ |
|||||
// <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 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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,79 +0,0 @@ |
|||||
// <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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,262 +0,0 @@ |
|||||
// <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() |
|
||||
{ |
|
||||
#if SILVERLIGHT
|
|
||||
Start(1); |
|
||||
#else
|
|
||||
Start(Environment.ProcessorCount); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
/// <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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,602 +0,0 @@ |
|||||
// <copyright file="ParallelTest.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.UnitTests.ThreadingTests |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading; |
|
||||
using MbUnit.Framework; |
|
||||
using Threading; |
|
||||
|
|
||||
[TestFixture] |
|
||||
public class ParallelForEachTests |
|
||||
{ |
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceMTAOnePerCore(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceSTAOnePerCore(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceMTATwoPerCore(int count) |
|
||||
{ |
|
||||
// ensure Two-Per-Core
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceSTATwoPerCore(int count) |
|
||||
{ |
|
||||
// ensure Two-Per-Core
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceMTAOnePerCore(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceSTAOnePerCore(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceMTATwoPerCore(int count) |
|
||||
{ |
|
||||
// ensure Two-Per-Core
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceSTATwoPerCore(int count) |
|
||||
{ |
|
||||
// ensure Two-Per-Core
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var items = new double[count]; |
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
Parallel.ForEach(pairs, pair => items[pair.Key] = pair.Value); |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
public void DoesNotGetConfusedByMultipleStartShutdown() |
|
||||
{ |
|
||||
ThreadQueue.Shutdown(); |
|
||||
ThreadQueue.Shutdown(); |
|
||||
|
|
||||
ThreadQueue.Start(2); |
|
||||
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
Control.NumberOfParallelWorkerThreads = 2; |
|
||||
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
ThreadQueue.Start(4); |
|
||||
Assert.AreEqual(4, ThreadQueue.ThreadCount); |
|
||||
Assert.AreEqual(4, Control.NumberOfParallelWorkerThreads); |
|
||||
|
|
||||
ThreadQueue.Shutdown(); |
|
||||
ThreadQueue.Start(); |
|
||||
Assert.AreEqual(4, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
ThreadQueue.Start(2); |
|
||||
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
var items = new double[50]; |
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
items[i] = i; |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, i)); |
|
||||
} |
|
||||
|
|
||||
Parallel.ForEach(pairs, |
|
||||
pair => |
|
||||
{ |
|
||||
items[pair.Key] = pair.Value + 1000; |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1000 + i, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
public void DoesDetectAndResolveRecursiveParallelization() |
|
||||
{ |
|
||||
var countSharedBetweenClosures = 0; |
|
||||
var values = new int[10]; |
|
||||
|
|
||||
Assert.DoesNotThrow( |
|
||||
() => |
|
||||
Parallel.ForEach(values, |
|
||||
j => Interlocked.Increment(ref countSharedBetweenClosures))); |
|
||||
|
|
||||
Assert.AreEqual(10, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.ForEach(values, |
|
||||
i => |
|
||||
Parallel.ForEach(values, |
|
||||
j => Interlocked.Increment(ref countSharedBetweenClosures))); |
|
||||
|
|
||||
Assert.AreEqual(100, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.ForEach(values, |
|
||||
i => |
|
||||
Parallel.ForEach(values, |
|
||||
j => |
|
||||
Parallel.ForEach(values, |
|
||||
k => Interlocked.Increment(ref countSharedBetweenClosures)))); |
|
||||
|
|
||||
Assert.AreEqual(1000, countSharedBetweenClosures); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceMTAOnePerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceSTAOnePerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceMTATwoPerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInListOnceSTATwoPerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new List<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceMTAOnePerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceSTAOnePerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceMTATwoPerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1001)] |
|
||||
public void ParallelForEachInvokesEveryItemInSetOnceSTATwoPerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var pairs = new HashSet<KeyValuePair<int, double>>(); |
|
||||
for (var i = 0; i < count; i++) |
|
||||
{ |
|
||||
pairs.Add(new KeyValuePair<int, double>(i, 2)); |
|
||||
} |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
var sync = new object(); |
|
||||
|
|
||||
Parallel.ForEach( |
|
||||
pairs, () => 0.0, (pair, localData) => localData += pair.Value, |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, Timeout(15)] |
|
||||
public void DoesDetectAndResolveRecursiveParallelizationWithIntialAndFinally() |
|
||||
{ |
|
||||
var countSharedBetweenClosures = 0; |
|
||||
var values = new int[10]; |
|
||||
|
|
||||
Assert.DoesNotThrow( |
|
||||
() => |
|
||||
Parallel.ForEach(values, |
|
||||
j => Interlocked.Increment(ref countSharedBetweenClosures))); |
|
||||
|
|
||||
Assert.AreEqual(10, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.ForEach(values, |
|
||||
i => |
|
||||
Parallel.ForEach(values, |
|
||||
j => Interlocked.Increment(ref countSharedBetweenClosures))); |
|
||||
|
|
||||
Assert.AreEqual(100, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.ForEach(values, |
|
||||
i => |
|
||||
Parallel.ForEach(values, |
|
||||
j => |
|
||||
Parallel.ForEach(values, () => 0.0, |
|
||||
(iterator, localData) => Interlocked.Increment(ref countSharedBetweenClosures), |
|
||||
localResult => { return; } |
|
||||
))); |
|
||||
|
|
||||
Assert.AreEqual(1000, countSharedBetweenClosures); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,364 +0,0 @@ |
|||||
// <copyright file="ParallelTest.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.UnitTests.ThreadingTests |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Threading; |
|
||||
using MbUnit.Framework; |
|
||||
using Threading; |
|
||||
|
|
||||
[TestFixture] |
|
||||
public class ParallelForTests |
|
||||
{ |
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceMTAOnePerCore(int count) |
|
||||
{ |
|
||||
var items = new int[count]; |
|
||||
|
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
Parallel.For(0, count, i => items[i]++); |
|
||||
Parallel.For(0, count, i => items[i] += 1000); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1001, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceSTAOnePerCore(int count) |
|
||||
{ |
|
||||
var items = new int[count]; |
|
||||
|
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
Parallel.For(0, count, i => items[i]++); |
|
||||
Parallel.For(0, count, i => items[i] += 1000); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1001, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceMTATwoPerCore(int count) |
|
||||
{ |
|
||||
var items = new int[count]; |
|
||||
|
|
||||
// ensure Two-Per-Core
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
Parallel.For(0, count, i => items[i]++); |
|
||||
Parallel.For(0, count, i => items[i] += 1000); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1001, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceSTATwoPerCore(int count) |
|
||||
{ |
|
||||
var items = new int[count]; |
|
||||
|
|
||||
// ensure Two-Per-Core
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
Parallel.For(0, count, i => items[i]++); |
|
||||
Parallel.For(0, count, i => items[i] += 1000); |
|
||||
|
|
||||
for (int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1001, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, MultipleAsserts, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
public void DoesNotGetConfusedByMultipleStartShutdown() |
|
||||
{ |
|
||||
ThreadQueue.Shutdown(); |
|
||||
ThreadQueue.Shutdown(); |
|
||||
|
|
||||
ThreadQueue.Start(2); |
|
||||
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
Control.NumberOfParallelWorkerThreads = 2; |
|
||||
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
ThreadQueue.Start(4); |
|
||||
Assert.AreEqual(4, ThreadQueue.ThreadCount); |
|
||||
Assert.AreEqual(4, Control.NumberOfParallelWorkerThreads); |
|
||||
|
|
||||
ThreadQueue.Shutdown(); |
|
||||
ThreadQueue.Start(); |
|
||||
Assert.AreEqual(4, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
ThreadQueue.Start(2); |
|
||||
Assert.AreEqual(2, ThreadQueue.ThreadCount); |
|
||||
|
|
||||
var items = new int[50]; |
|
||||
Parallel.For(0, items.Length, i => items[i]++); |
|
||||
|
|
||||
Parallel.For(0, items.Length, i => items[i] += 1000); |
|
||||
|
|
||||
ThreadQueue.Shutdown(); |
|
||||
|
|
||||
for(int i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
Assert.AreEqual(1001, items[i], i.ToString()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
public void DoesDetectAndResolveRecursiveParallelization() |
|
||||
{ |
|
||||
int countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Assert.DoesNotThrow( |
|
||||
() => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
j => Interlocked.Increment(ref countSharedBetweenClosures))); |
|
||||
|
|
||||
Assert.AreEqual(10, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
i => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
j => Interlocked.Increment(ref countSharedBetweenClosures))); |
|
||||
|
|
||||
Assert.AreEqual(100, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
i => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
j => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
k => Interlocked.Increment(ref countSharedBetweenClosures)))); |
|
||||
|
|
||||
Assert.AreEqual(1000, countSharedBetweenClosures); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceMTAOnePerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
var items = new double[count]; |
|
||||
|
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
for(var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
items[i] = 2; |
|
||||
} |
|
||||
var sync = new object(); |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
count, |
|
||||
() => 0.0, |
|
||||
(i, localData) =>localData += items[i], |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceSTAOnePerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
var items = new double[count]; |
|
||||
|
|
||||
// ensure One-Per-Core
|
|
||||
ThreadQueue.Start(Environment.ProcessorCount); |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
items[i] = 2; |
|
||||
} |
|
||||
var sync = new object(); |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
count, |
|
||||
() => 0.0, |
|
||||
(i, localData) => localData += items[i], |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceMTATwoPerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
var items = new double[count]; |
|
||||
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
items[i] = 2; |
|
||||
} |
|
||||
var sync = new object(); |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
count, |
|
||||
() => 0.0, |
|
||||
(i, localData) => localData += items[i], |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.STA), Timeout(15)] |
|
||||
[Column(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101)] |
|
||||
public void ParallelForInvokesEveryItemOnceSTATwoPerCoreWithIntialAndFinally(int count) |
|
||||
{ |
|
||||
var items = new double[count]; |
|
||||
|
|
||||
ThreadQueue.Start(2 * Environment.ProcessorCount); |
|
||||
|
|
||||
var sum = 0.0; |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
items[i] = 2; |
|
||||
} |
|
||||
var sync = new object(); |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
count, |
|
||||
() => 0.0, |
|
||||
(i, localData) => localData += items[i], |
|
||||
localResult => |
|
||||
{ |
|
||||
lock (sync) |
|
||||
{ |
|
||||
sum += localResult; |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
Assert.AreEqual(count * 2, sum); |
|
||||
} |
|
||||
|
|
||||
[Test, ApartmentState(ApartmentState.MTA), Timeout(15)] |
|
||||
public void DoesDetectAndResolveRecursiveParallelizationWithIntialAndFinally() |
|
||||
{ |
|
||||
int countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Assert.DoesNotThrow( |
|
||||
() => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
() => 0.0, |
|
||||
(j, localData) => Interlocked.Increment(ref countSharedBetweenClosures), |
|
||||
localResult => { return; } |
|
||||
)); |
|
||||
|
|
||||
Assert.AreEqual(10, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
i => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
() => 0.0, |
|
||||
(j, localData) => Interlocked.Increment(ref countSharedBetweenClosures), |
|
||||
localResult => { return; } |
|
||||
)); |
|
||||
|
|
||||
Assert.AreEqual(100, countSharedBetweenClosures); |
|
||||
countSharedBetweenClosures = 0; |
|
||||
|
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
i => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
j => |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
10, |
|
||||
() => 0.0, |
|
||||
(k, localData) => Interlocked.Increment(ref countSharedBetweenClosures), |
|
||||
localResult => { return; } |
|
||||
))); |
|
||||
|
|
||||
Assert.AreEqual(1000, countSharedBetweenClosures); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue