Browse Source

Normalize row action with buffer.

af/octree-no-pixelmap
James Jackson-South 6 years ago
parent
commit
cc2a4d5288
  1. 31
      src/ImageSharp/Advanced/ParallelUtils/IRowIntervalAction.cs
  2. 67
      src/ImageSharp/Advanced/ParallelUtils/IRowIntervalAction{TBuffer}.cs
  3. 125
      src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs
  4. 4
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

31
src/ImageSharp/Advanced/ParallelUtils/IRowIntervalAction.cs

@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced.ParallelUtils
{
/// <summary>
/// Defines the contract for.
/// Defines the contract for an action that operates on a row interval.
/// </summary>
public interface IRowIntervalAction
{
@ -21,15 +21,22 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
internal readonly struct WrappingRowIntervalInfo
{
public readonly int Min;
public readonly int Max;
public readonly int Step;
public readonly int MinY;
public readonly int MaxY;
public readonly int StepY;
public readonly int MaxX;
public WrappingRowIntervalInfo(int min, int max, int step)
public WrappingRowIntervalInfo(int minY, int maxY, int stepY)
: this(minY, maxY, stepY, 0)
{
this.Min = min;
this.Max = max;
this.Step = step;
}
public WrappingRowIntervalInfo(int minY, int maxY, int stepY, int maxX)
{
this.MinY = minY;
this.MaxY = maxY;
this.StepY = stepY;
this.MaxX = maxX;
}
}
@ -39,7 +46,7 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
private readonly WrappingRowIntervalInfo info;
private readonly T action;
public WrappingRowIntervalAction(in WrappingRowIntervalInfo info, ref T action)
public WrappingRowIntervalAction(in WrappingRowIntervalInfo info, in T action)
{
this.info = info;
this.action = action;
@ -48,14 +55,14 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.Min + (i * this.info.Step);
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.Max)
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.Step, this.info.Max);
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);

67
src/ImageSharp/Advanced/ParallelUtils/IRowIntervalAction{TBuffer}.cs

@ -0,0 +1,67 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced.ParallelUtils
{
/// <summary>
/// Defines the contract for an action that operates on a row interval with a temporary buffer.
/// </summary>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
public interface IRowIntervalAction<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Invokes the method passing the row interval and a buffer.
/// </summary>
/// <param name="rows">The row interval.</param>
/// <param name="memory">The contiguous region of memory.</param>
void Invoke(in RowInterval rows, Memory<TBuffer> memory);
}
internal readonly struct WrappingRowIntervalAction<T, TBuffer> : IRowIntervalAction<TBuffer>
where T : struct, IRowIntervalAction<TBuffer>
where TBuffer : unmanaged
{
private readonly WrappingRowIntervalInfo info;
private readonly MemoryAllocator allocator;
private readonly T action;
public WrappingRowIntervalAction(
in WrappingRowIntervalInfo info,
MemoryAllocator allocator,
in T action)
{
this.info = info;
this.allocator = allocator;
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.info.MinY + (i * this.info.StepY);
if (yMin >= this.info.MaxY)
{
return;
}
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
var rows = new RowInterval(yMin, yMax);
using (IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.MaxX))
{
this.Invoke(in rows, buffer.Memory);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<TBuffer> memory) => this.action.Invoke(in rows, memory);
}
}

125
src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs

@ -21,14 +21,16 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
/// <typeparam name="T">The type of row action to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows(Rectangle rectangle, Configuration configuration, Action<RowInterval> body)
[MethodImpl(InliningOptions.ShortMethod)]
public static void IterateRows<T>(Rectangle rectangle, Configuration configuration, in T body)
where T : struct, IRowIntervalAction
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, in parallelSettings, body);
IterateRows(rectangle, in parallelSettings, in body);
}
/// <summary>
@ -36,52 +38,120 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
/// </summary>
/// <typeparam name="T">The type of row action to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRowsFast<T>(Rectangle rectangle, Configuration configuration, ref T body)
where T : struct, IRowIntervalAction
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRowsFast(rectangle, in parallelSettings, ref body);
}
internal static void IterateRowsFast<T>(
public static void IterateRows<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
ref T body)
in T body)
where T : struct, IRowIntervalAction
{
ValidateRectangle(rectangle);
int maxSteps = DivideCeil(
rectangle.Width * rectangle.Height,
parallelSettings.MinimumPixelsProcessedPerTask);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
var rows = new RowInterval(rectangle.Top, rectangle.Bottom);
var rows = new RowInterval(top, bottom);
body.Invoke(in rows);
return;
}
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep);
var rowAction = new WrappingRowIntervalAction<T>(in rowInfo, in body);
Parallel.For(
0,
numOfSteps,
parallelOptions,
i => rowAction.Invoke(i));
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row action to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T, TBuffer>(Rectangle rectangle, Configuration configuration, in T body)
where T : struct, IRowIntervalAction<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows<T, TBuffer>(rectangle, in parallelSettings, in body);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary>
internal static void IterateRows<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T body)
where T : struct, IRowIntervalAction<TBuffer>
where TBuffer : unmanaged
{
ValidateRectangle(rectangle);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep);
var rowAction = new WrappingRowIntervalAction<T>(in rowInfo, ref body);
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
var rows = new RowInterval(top, bottom);
using (IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width))
{
body.Invoke(rows, buffer.Memory);
}
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var rowInfo = new WrappingRowIntervalInfo(top, bottom, verticalStep, width);
var rowAction = new WrappingRowIntervalAction<T, TBuffer>(in rowInfo, allocator, in body);
Parallel.For(
0,
numOfSteps,
parallelOptions,
i => rowAction.Invoke(i));
i =>
rowAction.Invoke(i));
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
// [Obsolete("Use non-allocating generic versions instead.")]
public static void IterateRows(Rectangle rectangle, Configuration configuration, Action<RowInterval> body)
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, in parallelSettings, body);
}
/// <summary>
@ -90,10 +160,11 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
// [Obsolete("Use non-allocating generic versions instead.")]
public static void IterateRows(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action<RowInterval> body)
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action<RowInterval> body)
{
ValidateRectangle(rectangle);
@ -143,6 +214,7 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary>
// [Obsolete("Use non-allocating generic versions instead.")]
internal static void IterateRowsWithTempBuffer<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
@ -204,6 +276,7 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="body"/> invocation.
/// </summary>
// [Obsolete("Use non-allocating generic versions instead.")]
internal static void IterateRowsWithTempBuffer<T>(
Rectangle rectangle,
Configuration configuration,
@ -213,7 +286,7 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor);
private static void ValidateRectangle(Rectangle rectangle)

4
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -54,10 +54,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
var rowAction = new RowAction(ref bounds, source, destination);
ParallelHelper.IterateRowsFast(
ParallelHelper.IterateRows(
bounds,
in parallelSettings,
ref rowAction);
in rowAction);
}
private readonly struct RowAction : IRowIntervalAction

Loading…
Cancel
Save