Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
aaa5bb7
Add row action overload
JimBobSquarePants Feb 5, 2020
b6d8375
Merge branch 'master' into js/parallel-allocation-experiments
JimBobSquarePants Feb 5, 2020
4523366
Reduce captuing allocation to 16 bytes.
JimBobSquarePants Feb 5, 2020
463f0a9
Add memorydiagnoser
JimBobSquarePants Feb 5, 2020
abb064f
Normalize row action with buffer.
JimBobSquarePants Feb 5, 2020
e3a7bb4
Remove final allocations
JimBobSquarePants Feb 5, 2020
62890f8
Rename class and add native memory profiler
JimBobSquarePants Feb 6, 2020
7f19fb2
Convert AffineTranformProcessor
JimBobSquarePants Feb 6, 2020
01f2c05
Update ProjectiveTransformProcessor{TPixel}.cs
JimBobSquarePants Feb 6, 2020
e40731d
Internalize, partially optimize and rename Action methods.
JimBobSquarePants Feb 6, 2020
3b3e6ab
Fix non-windows build
JimBobSquarePants Feb 6, 2020
079e2a6
Add XML comments to the CropProcessor<TPixel>
Sergio0694 Feb 6, 2020
2f83149
Refactor BokehBlurProcessor<TPixel>
Sergio0694 Feb 6, 2020
6298c78
Refactor Convolution2PassProcessor<TPixel>
Sergio0694 Feb 6, 2020
c5ed869
Refactor ConvolutionProcessor<TPixel>
Sergio0694 Feb 6, 2020
58418b4
Refactor EdgeDetectorCompassProcessor<TPixel>
Sergio0694 Feb 6, 2020
1564149
Refactor Convolution2DProcessor<TPixel>
Sergio0694 Feb 6, 2020
d844f8a
Add readonly modifiers to APIs in Rectangle
Sergio0694 Feb 6, 2020
2bc5d82
Removed unnecessary XML comments
Sergio0694 Feb 6, 2020
b77a27c
Refactor DrawImageProcessor<TPixelBg, TPixelFg>
Sergio0694 Feb 6, 2020
d5d50e9
Refactor OilPaintingProcessor<TPixel>
Sergio0694 Feb 6, 2020
f49d1b6
Refactor PixelRowDelegateProcessorBase<TPixel>
Sergio0694 Feb 6, 2020
6d4d9a6
Major refactor to the pixel row delegate processors
Sergio0694 Feb 6, 2020
d39a057
Refactor FilterProcessor
Sergio0694 Feb 7, 2020
c6c6b9b
Minor perf update
JimBobSquarePants Feb 7, 2020
e9e461e
Update ProjectiveTransformProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
d7bce16
Update BackgroundColorProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
c01889f
Update GlowProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
4cf5920
Update VignetteProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
57acad2
Update GlowProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
70f0e9a
Update FlipProcessor
JimBobSquarePants Feb 7, 2020
e771c50
Update ResizeProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
5083818
Seal ResizeWorker
JimBobSquarePants Feb 7, 2020
57e03ae
Update RotateProcessor{TPixel}.cs
JimBobSquarePants Feb 7, 2020
add9613
Refactor GlobalHistogramEqualizationProcessor<TPixel>
Sergio0694 Feb 7, 2020
d274142
Refactor ImageFrame<TPixel>
Sergio0694 Feb 7, 2020
6b9d344
Refactor BinaryThresholdProcessor<TPixel>
Sergio0694 Feb 7, 2020
dd7bd7b
Skip some safety readonly struct copies
Sergio0694 Feb 7, 2020
b77dcfa
Add single row RowAction value delegate
Sergio0694 Feb 7, 2020
a0ad4ce
Refactor processors to single row logic
Sergio0694 Feb 7, 2020
d6fb30e
Refactor single row APIs
Sergio0694 Feb 7, 2020
dd4285d
Add single row value delegate with buffer
Sergio0694 Feb 7, 2020
3da200d
Refactor processors to single row with buffer APIs
Sergio0694 Feb 7, 2020
b545ea0
Rename APIs
Sergio0694 Feb 7, 2020
1835475
Merge pull request #1108 from SixLabors/sp/single-row-parallel-value-…
JimBobSquarePants Feb 8, 2020
fcf7c44
Update pixelate processor
JimBobSquarePants Feb 8, 2020
8540f83
Revert "Merge pull request #1108 from SixLabors/sp/single-row-paralle…
JimBobSquarePants Feb 8, 2020
89e3898
Update PixelateProcessor{TPixel}.cs
JimBobSquarePants Feb 8, 2020
2b00433
Update AdaptiveHistogramEqualizationProcessor and rename interfaces
JimBobSquarePants Feb 8, 2020
7a8edff
Update AdaptiveHistogramEqualizationProcessor{TPixel}.cs
JimBobSquarePants Feb 8, 2020
7609a53
Update AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
JimBobSquarePants Feb 9, 2020
c946849
Fix arguments order and tests
JimBobSquarePants Feb 9, 2020
b247bac
Use Span<T> as buffer param
JimBobSquarePants Feb 9, 2020
c5166c9
Update ProjectiveTransformProcessor{TPixel}.cs
JimBobSquarePants Feb 10, 2020
3cfe575
Update EdgeDetectorCompassProcessor{TPixel}.cs
JimBobSquarePants Feb 10, 2020
49d0215
Update BinaryThresholdProcessor{TPixel}.cs
JimBobSquarePants Feb 10, 2020
eefa57e
Cleanup
JimBobSquarePants Feb 10, 2020
4b7718e
Undo unrelated changes and clean up.
JimBobSquarePants Feb 12, 2020
b5d055f
Update QuantizeProcessor{TPixel}.cs
JimBobSquarePants Feb 12, 2020
c66dd0c
Rename things
JimBobSquarePants Feb 12, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/ImageSharp/Advanced/IRowIntervalOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row interval.
/// </summary>
public interface IRowIntervalOperation
{
/// <summary>
/// Invokes the method passing the row interval.
/// </summary>
/// <param name="rows">The row interval.</param>
void Invoke(in RowInterval rows);
}
}
25 changes: 25 additions & 0 deletions src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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
{
/// <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 IRowIntervalOperation<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="span">The contiguous region of memory.</param>
void Invoke(in RowInterval rows, Span<TBuffer> span);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Threading.Tasks;

using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Advanced.ParallelUtils
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines execution settings for methods in <see cref="ParallelHelper"/>.
/// Defines execution settings for methods in <see cref="ParallelRowIterator"/>.
/// </summary>
public readonly struct ParallelExecutionSettings
{
Expand Down Expand Up @@ -89,7 +89,7 @@ public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier)
}

/// <summary>
/// Get the default <see cref="SixLabors.ImageSharp.Advanced.ParallelUtils.ParallelExecutionSettings"/> for a <see cref="SixLabors.ImageSharp.Configuration"/>
/// Get the default <see cref="SixLabors.ImageSharp.Advanced.ParallelExecutionSettings"/> for a <see cref="SixLabors.ImageSharp.Configuration"/>
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/>.</param>
/// <returns>The <see cref="ParallelExecutionSettings"/>.</returns>
Expand Down
110 changes: 110 additions & 0 deletions src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Advanced
{
/// <content>
/// Utility methods for batched processing of pixel row intervals.
/// Parallel execution is optimized for image processing based on values defined
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </content>
public static partial class ParallelRowIterator
{
private readonly struct IterationParameters
{
public readonly int MinY;
public readonly int MaxY;
public readonly int StepY;
public readonly int Width;

public IterationParameters(int minY, int maxY, int stepY)
: this(minY, maxY, stepY, 0)
{
}

public IterationParameters(int minY, int maxY, int stepY, int width)
{
this.MinY = minY;
this.MaxY = maxY;
this.StepY = stepY;
this.Width = width;
}
}

private readonly struct RowIntervalOperationWrapper<T>
where T : struct, IRowIntervalOperation
{
private readonly IterationParameters info;
private readonly T operation;

[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(in IterationParameters info, in T operation)
{
this.info = info;
this.operation = operation;
}

[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);

// Skip the safety copy when invoking a potentially impure method on a readonly field
Unsafe.AsRef(in this.operation).Invoke(in rows);
}
}

private readonly struct RowIntervalOperationWrapper<T, TBuffer>
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly IterationParameters info;
private readonly MemoryAllocator allocator;
private readonly T operation;

[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(
in IterationParameters info,
MemoryAllocator allocator,
in T operation)
{
this.info = info;
this.allocator = allocator;
this.operation = operation;
}

[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.Width);

Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span);
}
}
}
}
162 changes: 162 additions & 0 deletions src/ImageSharp/Advanced/ParallelRowIterator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Utility methods for batched processing of pixel row intervals.
/// Parallel execution is optimized for image processing based on values defined
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </summary>
public static partial class ParallelRowIterator
{
/// <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 operation to perform.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IterateRows<T>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, in parallelSettings, in operation);
}

/// <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 operation to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowIntervalOperation
{
ValidateRectangle(rectangle);

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(top, bottom);
Unsafe.AsRef(in operation).Invoke(in rows);
return;
}

int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var info = new IterationParameters(top, bottom, verticalStep);
var wrappingOperation = new RowIntervalOperationWrapper<T>(in info, in operation);

Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}

/// <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="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows<T, TBuffer>(rectangle, in parallelSettings, in operation);
}

/// <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="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
ValidateRectangle(rectangle);

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);
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))
{
Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
}

return;
}

int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var info = new IterationParameters(top, bottom, verticalStep, width);
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(in info, allocator, in operation);

Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}

[MethodImpl(InliningOptions.ShortMethod)]
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor);

private static void ValidateRectangle(Rectangle rectangle)
{
Guard.MustBeGreaterThan(
rectangle.Width,
0,
$"{nameof(rectangle)}.{nameof(rectangle.Width)}");

Guard.MustBeGreaterThan(
rectangle.Height,
0,
$"{nameof(rectangle)}.{nameof(rectangle.Height)}");
}
}
}
Loading