//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2013 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.
//
using System;
using System.Collections.Generic;
#if !NETSTANDARD1_3
using System.Threading.Tasks;
using System.Collections.Concurrent;
#endif
namespace IStation.Numerics.Threading
{
///
/// Used to simplify parallel code, particularly between the .NET 4.0 and Silverlight Code.
///
internal static class CommonParallel
{
static ParallelOptions CreateParallelOptions()
{
return new ParallelOptions
{
MaxDegreeOfParallelism = Control.MaxDegreeOfParallelism,
TaskScheduler = Control.TaskScheduler,
};
}
///
/// Executes a for loop in which iterations may run in parallel.
///
/// The start index, inclusive.
/// The end index, exclusive.
/// The body to be invoked for each iteration range.
public static void For(int fromInclusive, int toExclusive, Action body)
{
For(fromInclusive, toExclusive, Math.Max(1, (toExclusive - fromInclusive)/Control.MaxDegreeOfParallelism), body);
}
///
/// Executes a for loop in which iterations may run in parallel.
///
/// The start index, inclusive.
/// The end index, exclusive.
/// The partition size for splitting work into smaller pieces.
/// The body to be invoked for each iteration range.
public static void For(int fromInclusive, int toExclusive, int rangeSize, Action body)
{
if (body == null)
{
throw new ArgumentNullException(nameof(body));
}
if (fromInclusive < 0)
{
throw new ArgumentOutOfRangeException(nameof(fromInclusive));
}
if (fromInclusive > toExclusive)
{
throw new ArgumentOutOfRangeException(nameof(toExclusive));
}
if (rangeSize < 1)
{
throw new ArgumentOutOfRangeException(nameof(rangeSize));
}
var length = toExclusive - fromInclusive;
// Special case: nothing to do
if (length <= 0)
{
return;
}
// Special case: not worth to parallelize, inline
if (Control.MaxDegreeOfParallelism < 2 || (rangeSize*2) > length)
{
body(fromInclusive, toExclusive);
return;
}
// Common case
Parallel.ForEach(
Partitioner.Create(fromInclusive, toExclusive, rangeSize),
CreateParallelOptions(),
range => body(range.Item1, range.Item2));
}
///
/// Executes each of the provided actions inside a discrete, asynchronous task.
///
/// An array of actions to execute.
/// The actions array contains a null element.
/// At least one invocation of the actions threw an exception.
public static void Invoke(params Action[] actions)
{
// Special case: no action
if (actions.Length == 0)
{
return;
}
// Special case: single action, inline
if (actions.Length == 1)
{
actions[0]();
return;
}
// Special case: straight execution without parallelism
if (Control.MaxDegreeOfParallelism < 2)
{
for (int i = 0; i < actions.Length; i++)
{
actions[i]();
}
return;
}
// Common case
Parallel.Invoke(
CreateParallelOptions(),
actions);
}
///
/// Selects an item (such as Max or Min).
///
/// Starting index of the loop.
/// Ending index of the loop
/// The function to select items over a subset.
/// The function to select the item of selection from the subsets.
/// The selected value.
public static T Aggregate(int fromInclusive, int toExclusive, Func select, Func reduce)
{
if (select == null)
{
throw new ArgumentNullException(nameof(select));
}
if (reduce == null)
{
throw new ArgumentNullException(nameof(reduce));
}
// Special case: no action
if (fromInclusive >= toExclusive)
{
return reduce(new T[0]);
}
// Special case: single action, inline
if (fromInclusive == (toExclusive - 1))
{
return reduce(new[] { select(fromInclusive) });
}
// Special case: straight execution without parallelism
if (Control.MaxDegreeOfParallelism < 2)
{
var mapped = new T[toExclusive - fromInclusive];
for (int k = 0; k < mapped.Length; k++)
{
mapped[k] = select(k + fromInclusive);
}
return reduce(mapped);
}
// Common case
var intermediateResults = new List();
var syncLock = new object();
Parallel.ForEach(
Partitioner.Create(fromInclusive, toExclusive),
CreateParallelOptions(),
() => new List(),
(range, loop, localData) =>
{
var mapped = new T[range.Item2 - range.Item1];
for (int k = 0; k < mapped.Length; k++)
{
mapped[k] = select(k + range.Item1);
}
localData.Add(reduce(mapped));
return localData;
},
localResult =>
{
lock (syncLock)
{
intermediateResults.Add(reduce(localResult.ToArray()));
}
});
return reduce(intermediateResults.ToArray());
}
///
/// Selects an item (such as Max or Min).
///
/// The array to iterate over.
/// The function to select items over a subset.
/// The function to select the item of selection from the subsets.
/// The selected value.
public static TOut Aggregate(T[] array, Func select, Func reduce)
{
if (select == null)
{
throw new ArgumentNullException(nameof(select));
}
if (reduce == null)
{
throw new ArgumentNullException(nameof(reduce));
}
// Special case: no action
if (array == null || array.Length == 0)
{
return reduce(new TOut[0]);
}
// Special case: single action, inline
if (array.Length == 1)
{
return reduce(new[] { select(0, array[0]) });
}
// Special case: straight execution without parallelism
if (Control.MaxDegreeOfParallelism < 2)
{
var mapped = new TOut[array.Length];
for (int k = 0; k < mapped.Length; k++)
{
mapped[k] = select(k, array[k]);
}
return reduce(mapped);
}
// Common case
var intermediateResults = new List();
var syncLock = new object();
Parallel.ForEach(
Partitioner.Create(0, array.Length),
CreateParallelOptions(),
() => new List(),
(range, loop, localData) =>
{
var mapped = new TOut[range.Item2 - range.Item1];
for (int k = 0; k < mapped.Length; k++)
{
mapped[k] = select(k + range.Item1, array[k + range.Item1]);
}
localData.Add(reduce(mapped));
return localData;
},
localResult =>
{
lock (syncLock)
{
intermediateResults.Add(reduce(localResult.ToArray()));
}
});
return reduce(intermediateResults.ToArray());
}
///
/// Selects an item (such as Max or Min).
///
/// Starting index of the loop.
/// Ending index of the loop
/// The function to select items over a subset.
/// The function to select the item of selection from the subsets.
/// Default result of the reduce function on an empty set.
/// The selected value.
public static T Aggregate(int fromInclusive, int toExclusive, Func select, Func reducePair, T reduceDefault)
{
return Aggregate(fromInclusive, toExclusive, select, results =>
{
if (results == null || results.Length == 0)
{
return reduceDefault;
}
if (results.Length == 1)
{
return results[0];
}
T result = results[0];
for (int i = 1; i < results.Length; i++)
{
result = reducePair(result, results[i]);
}
return result;
});
}
///
/// Selects an item (such as Max or Min).
///
/// The array to iterate over.
/// The function to select items over a subset.
/// The function to select the item of selection from the subsets.
/// Default result of the reduce function on an empty set.
/// The selected value.
public static TOut Aggregate(T[] array, Func select, Func reducePair, TOut reduceDefault)
{
return Aggregate(array, select, results =>
{
if (results == null || results.Length == 0)
{
return reduceDefault;
}
if (results.Length == 1)
{
return results[0];
}
TOut result = results[0];
for (int i = 1; i < results.Length; i++)
{
result = reducePair(result, results[i]);
}
return result;
});
}
}
}