PageRenderTime 23ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Orchard.Web/Modules/Piedone.HelpfulLibraries/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Scan.cs

http://associativy.codeplex.com
C# | 218 lines | 108 code | 19 blank | 91 comment | 18 complexity | 019ffb83ec427d44ccd48cf7e4f7466d MD5 | raw file
Possible License(s): LGPL-2.1, MIT, BSD-3-Clause, Apache-2.0, CPL-1.0, CC-BY-SA-3.0, GPL-2.0
  1. //--------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. //
  5. // File: ParallelAlgorithms_Scan.cs
  6. //
  7. //--------------------------------------------------------------------------
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Threading.Tasks;
  11. namespace System.Threading.Algorithms
  12. {
  13. public static partial class ParallelAlgorithms
  14. {
  15. /// <summary>Computes a parallel prefix scan over the source enumerable using the specified function.</summary>
  16. /// <typeparam name="T">The type of the data in the source.</typeparam>
  17. /// <param name="source">The source data over which a prefix scan should be computed.</param>
  18. /// <param name="function">The function to use for the scan.</param>
  19. /// <returns>The results of the scan operation.</returns>
  20. /// <remarks>
  21. /// For very small functions, such as additions, an implementation targeted
  22. /// at the relevant type and operation will perform significantly better than
  23. /// this generalized implementation.
  24. /// </remarks>
  25. public static T[] Scan<T>(IEnumerable<T> source, Func<T, T, T> function)
  26. {
  27. return Scan(source, function, loadBalance: false);
  28. }
  29. /// <summary>Computes a parallel prefix scan over the source enumerable using the specified function.</summary>
  30. /// <typeparam name="T">The type of the data in the source.</typeparam>
  31. /// <param name="source">The source data over which a prefix scan should be computed.</param>
  32. /// <param name="function">The function to use for the scan.</param>
  33. /// <param name="loadBalance">Whether to load-balance during process.</param>
  34. /// <returns>The results of the scan operation.</returns>
  35. /// <remarks>
  36. /// For very small functions, such as additions, an implementation targeted
  37. /// at the relevant type and operation will perform significantly better than
  38. /// this generalized implementation.
  39. /// </remarks>
  40. public static T[] Scan<T>(IEnumerable<T> source, Func<T, T, T> function, bool loadBalance)
  41. {
  42. // Validate arguments
  43. if (source == null) throw new ArgumentNullException("source");
  44. // Create output copy
  45. var output = source.ToArray();
  46. // Do the prefix scan in-place on the copy and return the results
  47. ScanInPlace(output, function, loadBalance);
  48. return output;
  49. }
  50. /// <summary>Computes a parallel prefix scan in-place on an array using the specified function.</summary>
  51. /// <typeparam name="T">The type of the data in the source.</typeparam>
  52. /// <param name="data">The data over which a prefix scan should be computed. Upon exit, stores the results.</param>
  53. /// <param name="function">The function to use for the scan.</param>
  54. /// <returns>The results of the scan operation.</returns>
  55. /// <remarks>
  56. /// For very small functions, such as additions, an implementation targeted
  57. /// at the relevant type and operation will perform significantly better than
  58. /// this generalized implementation.
  59. /// </remarks>
  60. public static void ScanInPlace<T>(T[] data, Func<T, T, T> function)
  61. {
  62. ScanInPlace(data, function, loadBalance:false);
  63. }
  64. /// <summary>Computes a parallel prefix scan in-place on an array using the specified function.</summary>
  65. /// <typeparam name="T">The type of the data in the source.</typeparam>
  66. /// <param name="data">The data over which a prefix scan should be computed. Upon exit, stores the results.</param>
  67. /// <param name="function">The function to use for the scan.</param>
  68. /// <param name="loadBalance">Whether to load-balance during process.</param>
  69. /// <returns>The results of the scan operation.</returns>
  70. /// <remarks>
  71. /// For very small functions, such as additions, an implementation targeted
  72. /// at the relevant type and operation will perform significantly better than
  73. /// this generalized implementation.
  74. /// </remarks>
  75. public static void ScanInPlace<T>(T [] data, Func<T, T, T> function, bool loadBalance)
  76. {
  77. // Validate arguments
  78. if (data == null) throw new ArgumentNullException("data");
  79. if (function == null) throw new ArgumentNullException("function");
  80. // Do the prefix scan in-place and return the results. This implementation
  81. // of parallel prefix scan ends up executing the function twice as many
  82. // times as the sequential implementation. Thus, only if we have more than 2 cores
  83. // will the parallel version have a chance of running faster than sequential.
  84. if (Environment.ProcessorCount <= 2)
  85. {
  86. InclusiveScanInPlaceSerial(data, function, 0, data.Length, 1);
  87. }
  88. else if (loadBalance)
  89. {
  90. InclusiveScanInPlaceWithLoadBalancingParallel(data, function, 0, data.Length, 1);
  91. }
  92. else // parallel, non-loadbalance
  93. {
  94. InclusiveScanInPlaceParallel(data, function);
  95. }
  96. }
  97. /// <summary>Computes a sequential prefix scan over the array using the specified function.</summary>
  98. /// <typeparam name="T">The type of the data in the array.</typeparam>
  99. /// <param name="arr">The data, which will be overwritten with the computed prefix scan.</param>
  100. /// <param name="function">The function to use for the scan.</param>
  101. /// <param name="arrStart">The start of the data in arr over which the scan is being computed.</param>
  102. /// <param name="arrLength">The length of the data in arr over which the scan is being computed.</param>
  103. /// <param name="skip">The inclusive distance between elements over which the scan is being computed.</param>
  104. /// <remarks>No parameter validation is performed.</remarks>
  105. private static void InclusiveScanInPlaceSerial<T>(T[] arr, Func<T, T, T> function, int arrStart, int arrLength, int skip)
  106. {
  107. for (int i = arrStart; i + skip < arrLength; i += skip)
  108. {
  109. arr[i + skip] = function(arr[i], arr[i + skip]);
  110. }
  111. }
  112. /// <summary>Computes a sequential exclusive prefix scan over the array using the specified function.</summary>
  113. /// <param name="arr">The data, which will be overwritten with the computed prefix scan.</param>
  114. /// <param name="function">The function to use for the scan.</param>
  115. /// <param name="lowerBoundInclusive">The inclusive lower bound of the array at which to start the scan.</param>
  116. /// <param name="upperBoundExclusive">The exclusive upper bound of the array at which to end the scan.</param>
  117. public static void ExclusiveScanInPlaceSerial<T>(T[] arr, Func<T, T, T> function, int lowerBoundInclusive, int upperBoundExclusive)
  118. {
  119. T total = arr[lowerBoundInclusive];
  120. arr[lowerBoundInclusive] = default(T);
  121. for (int i = lowerBoundInclusive + 1; i < upperBoundExclusive; i++)
  122. {
  123. T prevTotal = total;
  124. total = function(total, arr[i]);
  125. arr[i] = prevTotal;
  126. }
  127. }
  128. /// <summary>Computes a parallel prefix scan over the array using the specified function.</summary>
  129. /// <typeparam name="T">The type of the data in the array.</typeparam>
  130. /// <param name="arr">The data, which will be overwritten with the computed prefix scan.</param>
  131. /// <param name="function">The function to use for the scan.</param>
  132. /// <param name="arrStart">The start of the data in arr over which the scan is being computed.</param>
  133. /// <param name="arrLength">The length of the data in arr over which the scan is being computed.</param>
  134. /// <param name="skip">The inclusive distance between elements over which the scan is being computed.</param>
  135. /// <remarks>No parameter validation is performed.</remarks>
  136. private static void InclusiveScanInPlaceWithLoadBalancingParallel<T>(T[] arr, Func<T, T, T> function,
  137. int arrStart, int arrLength, int skip)
  138. {
  139. // If the length is 0 or 1, just return a copy of the original array.
  140. if (arrLength <= 1) return;
  141. int halfInputLength = arrLength / 2;
  142. // Pairwise combine. Use static partitioning, as the function
  143. // is likely to be very small.
  144. Parallel.For(0, halfInputLength, i =>
  145. {
  146. int loc = arrStart + (i * 2 * skip);
  147. arr[loc + skip] = function(arr[loc], arr[loc + skip]);
  148. });
  149. // Recursively prefix scan the pairwise computations.
  150. InclusiveScanInPlaceWithLoadBalancingParallel(arr, function, arrStart + skip, halfInputLength, skip * 2);
  151. // Generate output. As before, use static partitioning.
  152. Parallel.For(0, (arrLength % 2) == 0 ? halfInputLength - 1 : halfInputLength, i =>
  153. {
  154. int loc = arrStart + (i * 2 * skip) + skip;
  155. arr[loc + skip] = function(arr[loc], arr[loc + skip]);
  156. });
  157. }
  158. /// <summary>Computes a parallel inclusive prefix scan over the array using the specified function.</summary>
  159. public static void InclusiveScanInPlaceParallel<T>(T[] arr, Func<T, T, T> function)
  160. {
  161. int procCount = Environment.ProcessorCount;
  162. T[] intermediatePartials = new T[procCount];
  163. using (var phaseBarrier = new Barrier(procCount,
  164. _ => ExclusiveScanInPlaceSerial(intermediatePartials, function, 0, intermediatePartials.Length)))
  165. {
  166. // Compute the size of each range
  167. int rangeSize = arr.Length / procCount;
  168. int nextRangeStart = 0;
  169. // Create, store, and wait on all of the tasks
  170. var tasks = new Task[procCount];
  171. for (int i = 0; i < procCount; i++, nextRangeStart += rangeSize)
  172. {
  173. // Get the range for each task, then start it
  174. int rangeNum = i;
  175. int lowerRangeInclusive = nextRangeStart;
  176. int upperRangeExclusive = i < procCount - 1 ? nextRangeStart + rangeSize : arr.Length;
  177. tasks[rangeNum] = Task.Factory.StartNew(() =>
  178. {
  179. // Phase 1: Prefix scan assigned range, and copy upper bound to intermediate partials
  180. InclusiveScanInPlaceSerial(arr, function, lowerRangeInclusive, upperRangeExclusive, 1);
  181. intermediatePartials[rangeNum] = arr[upperRangeExclusive - 1];
  182. // Phase 2: One thread only should prefix scan the intermediaries... done implicitly by the barrier
  183. phaseBarrier.SignalAndWait();
  184. // Phase 3: Incorporate partials
  185. if (rangeNum != 0)
  186. {
  187. for (int j = lowerRangeInclusive; j < upperRangeExclusive; j++)
  188. {
  189. arr[j] = function(intermediatePartials[rangeNum], arr[j]);
  190. }
  191. }
  192. });
  193. }
  194. // Wait for all of the tasks to complete
  195. Task.WaitAll(tasks);
  196. }
  197. }
  198. }
  199. }