|
|
|
@ -44,14 +44,14 @@ public static partial class ParallelRowIterator |
|
|
|
where T : struct, IRowOperation |
|
|
|
{ |
|
|
|
ValidateRectangle(rectangle); |
|
|
|
ValidateSettings(parallelSettings); |
|
|
|
|
|
|
|
int top = rectangle.Top; |
|
|
|
int bottom = rectangle.Bottom; |
|
|
|
int width = rectangle.Width; |
|
|
|
int height = rectangle.Height; |
|
|
|
|
|
|
|
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); |
|
|
|
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
|
|
int numOfSteps = GetNumberOfSteps(width, height, parallelSettings); |
|
|
|
|
|
|
|
// Avoid TPL overhead in this trivial case:
|
|
|
|
if (numOfSteps == 1) |
|
|
|
@ -65,7 +65,7 @@ public static partial class ParallelRowIterator |
|
|
|
} |
|
|
|
|
|
|
|
int verticalStep = DivideCeil(rectangle.Height, numOfSteps); |
|
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps }; |
|
|
|
ParallelOptions parallelOptions = CreateParallelOptions(parallelSettings, numOfSteps); |
|
|
|
RowOperationWrapper<T> wrappingOperation = new(top, bottom, verticalStep, in operation); |
|
|
|
|
|
|
|
_ = Parallel.For( |
|
|
|
@ -109,14 +109,14 @@ public static partial class ParallelRowIterator |
|
|
|
where TBuffer : unmanaged |
|
|
|
{ |
|
|
|
ValidateRectangle(rectangle); |
|
|
|
ValidateSettings(parallelSettings); |
|
|
|
|
|
|
|
int top = rectangle.Top; |
|
|
|
int bottom = rectangle.Bottom; |
|
|
|
int width = rectangle.Width; |
|
|
|
int height = rectangle.Height; |
|
|
|
|
|
|
|
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); |
|
|
|
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
|
|
int numOfSteps = GetNumberOfSteps(width, height, parallelSettings); |
|
|
|
MemoryAllocator allocator = parallelSettings.MemoryAllocator; |
|
|
|
int bufferLength = Unsafe.AsRef(in operation).GetRequiredBufferLength(rectangle); |
|
|
|
|
|
|
|
@ -135,7 +135,7 @@ public static partial class ParallelRowIterator |
|
|
|
} |
|
|
|
|
|
|
|
int verticalStep = DivideCeil(height, numOfSteps); |
|
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps }; |
|
|
|
ParallelOptions parallelOptions = CreateParallelOptions(parallelSettings, numOfSteps); |
|
|
|
RowOperationWrapper<T, TBuffer> wrappingOperation = new(top, bottom, verticalStep, bufferLength, allocator, in operation); |
|
|
|
|
|
|
|
_ = Parallel.For( |
|
|
|
@ -174,14 +174,14 @@ public static partial class ParallelRowIterator |
|
|
|
where T : struct, IRowIntervalOperation |
|
|
|
{ |
|
|
|
ValidateRectangle(rectangle); |
|
|
|
ValidateSettings(parallelSettings); |
|
|
|
|
|
|
|
int top = rectangle.Top; |
|
|
|
int bottom = rectangle.Bottom; |
|
|
|
int width = rectangle.Width; |
|
|
|
int height = rectangle.Height; |
|
|
|
|
|
|
|
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); |
|
|
|
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
|
|
int numOfSteps = GetNumberOfSteps(width, height, parallelSettings); |
|
|
|
|
|
|
|
// Avoid TPL overhead in this trivial case:
|
|
|
|
if (numOfSteps == 1) |
|
|
|
@ -192,7 +192,7 @@ public static partial class ParallelRowIterator |
|
|
|
} |
|
|
|
|
|
|
|
int verticalStep = DivideCeil(rectangle.Height, numOfSteps); |
|
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps }; |
|
|
|
ParallelOptions parallelOptions = CreateParallelOptions(parallelSettings, numOfSteps); |
|
|
|
RowIntervalOperationWrapper<T> wrappingOperation = new(top, bottom, verticalStep, in operation); |
|
|
|
|
|
|
|
_ = Parallel.For( |
|
|
|
@ -236,14 +236,14 @@ public static partial class ParallelRowIterator |
|
|
|
where TBuffer : unmanaged |
|
|
|
{ |
|
|
|
ValidateRectangle(rectangle); |
|
|
|
ValidateSettings(parallelSettings); |
|
|
|
|
|
|
|
int top = rectangle.Top; |
|
|
|
int bottom = rectangle.Bottom; |
|
|
|
int width = rectangle.Width; |
|
|
|
int height = rectangle.Height; |
|
|
|
|
|
|
|
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); |
|
|
|
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
|
|
int numOfSteps = GetNumberOfSteps(width, height, parallelSettings); |
|
|
|
MemoryAllocator allocator = parallelSettings.MemoryAllocator; |
|
|
|
int bufferLength = Unsafe.AsRef(in operation).GetRequiredBufferLength(rectangle); |
|
|
|
|
|
|
|
@ -259,7 +259,7 @@ public static partial class ParallelRowIterator |
|
|
|
} |
|
|
|
|
|
|
|
int verticalStep = DivideCeil(height, numOfSteps); |
|
|
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps }; |
|
|
|
ParallelOptions parallelOptions = CreateParallelOptions(parallelSettings, numOfSteps); |
|
|
|
RowIntervalOperationWrapper<T, TBuffer> wrappingOperation = new(top, bottom, verticalStep, bufferLength, allocator, in operation); |
|
|
|
|
|
|
|
_ = Parallel.For( |
|
|
|
@ -272,6 +272,37 @@ public static partial class ParallelRowIterator |
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
private static int DivideCeil(long dividend, int divisor) => (int)Math.Min(1 + ((dividend - 1) / divisor), int.MaxValue); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Creates the <see cref="ParallelOptions"/> for the current iteration.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="parallelSettings">The execution settings.</param>
|
|
|
|
/// <param name="numOfSteps">The number of row partitions to execute.</param>
|
|
|
|
/// <returns>The <see cref="ParallelOptions"/> instance.</returns>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
private static ParallelOptions CreateParallelOptions(in ParallelExecutionSettings parallelSettings, int numOfSteps) |
|
|
|
=> new() { MaxDegreeOfParallelism = parallelSettings.MaxDegreeOfParallelism == -1 ? -1 : numOfSteps }; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Calculates the number of row partitions to execute for the given region.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="width">The width of the region.</param>
|
|
|
|
/// <param name="height">The height of the region.</param>
|
|
|
|
/// <param name="parallelSettings">The execution settings.</param>
|
|
|
|
/// <returns>The number of row partitions to execute.</returns>
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
private static int GetNumberOfSteps(int width, int height, in ParallelExecutionSettings parallelSettings) |
|
|
|
{ |
|
|
|
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); |
|
|
|
|
|
|
|
if (parallelSettings.MaxDegreeOfParallelism == -1) |
|
|
|
{ |
|
|
|
// Row batching cannot produce more useful partitions than the number of rows available.
|
|
|
|
return Math.Min(height, maxSteps); |
|
|
|
} |
|
|
|
|
|
|
|
return Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); |
|
|
|
} |
|
|
|
|
|
|
|
private static void ValidateRectangle(Rectangle rectangle) |
|
|
|
{ |
|
|
|
Guard.MustBeGreaterThan( |
|
|
|
@ -284,4 +315,35 @@ public static partial class ParallelRowIterator |
|
|
|
0, |
|
|
|
$"{nameof(rectangle)}.{nameof(rectangle.Height)}"); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Validates the supplied <see cref="ParallelExecutionSettings"/>.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="parallelSettings">The execution settings.</param>
|
|
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
|
|
|
/// Thrown when <see cref="ParallelExecutionSettings.MaxDegreeOfParallelism"/> or
|
|
|
|
/// <see cref="ParallelExecutionSettings.MinimumPixelsProcessedPerTask"/> is invalid.
|
|
|
|
/// </exception>
|
|
|
|
/// <exception cref="ArgumentNullException">
|
|
|
|
/// Thrown when <see cref="ParallelExecutionSettings.MemoryAllocator"/> is null.
|
|
|
|
/// This also guards the public <see cref="ParallelExecutionSettings"/> default value, which bypasses constructor validation.
|
|
|
|
/// </exception>
|
|
|
|
private static void ValidateSettings(in ParallelExecutionSettings parallelSettings) |
|
|
|
{ |
|
|
|
// ParallelExecutionSettings is a public struct, so callers can pass default and bypass constructor validation.
|
|
|
|
if (parallelSettings.MaxDegreeOfParallelism is 0 or < -1) |
|
|
|
{ |
|
|
|
throw new ArgumentOutOfRangeException( |
|
|
|
$"{nameof(parallelSettings)}.{nameof(ParallelExecutionSettings.MaxDegreeOfParallelism)}"); |
|
|
|
} |
|
|
|
|
|
|
|
Guard.MustBeGreaterThan( |
|
|
|
parallelSettings.MinimumPixelsProcessedPerTask, |
|
|
|
0, |
|
|
|
$"{nameof(parallelSettings)}.{nameof(ParallelExecutionSettings.MinimumPixelsProcessedPerTask)}"); |
|
|
|
|
|
|
|
Guard.NotNull( |
|
|
|
parallelSettings.MemoryAllocator, |
|
|
|
$"{nameof(parallelSettings)}.{nameof(ParallelExecutionSettings.MemoryAllocator)}"); |
|
|
|
} |
|
|
|
} |
|
|
|
|