// // 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; }); } } }