mirror of https://github.com/SixLabors/ImageSharp
7 changed files with 193 additions and 89 deletions
@ -0,0 +1,36 @@ |
|||
// Copyright(c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Threading.Tasks; |
|||
|
|||
namespace SixLabors.ImageSharp.ParallelUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Defines execution settings for methods in <see cref="ParallelHelper"/>.
|
|||
/// </summary>
|
|||
internal struct ParallelExecutionSettings |
|||
{ |
|||
public ParallelExecutionSettings(int maxDegreeOfParallelism, int minimumPixelsProcessedPerTask) |
|||
{ |
|||
this.MaxDegreeOfParallelism = maxDegreeOfParallelism; |
|||
this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; |
|||
} |
|||
|
|||
public ParallelExecutionSettings(int maxDegreeOfParallelism) |
|||
: this(maxDegreeOfParallelism, 2048) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value used for initializing <see cref="ParallelOptions.MaxDegreeOfParallelism"/> when using TPL.
|
|||
/// </summary>
|
|||
public int MaxDegreeOfParallelism { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the minimum number of pixels being processed by a single task when parallelizing operations with TPL.
|
|||
/// Launching tasks for pixel regions below this limit is not worth the overhead.
|
|||
/// Initialized with 2048 by default, the optimum value is operation specific.
|
|||
/// </summary>
|
|||
public int MinimumPixelsProcessedPerTask { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
// Copyright(c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading.Tasks; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.ParallelUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Utility methods wrapping Parallel.For() execution optimized for image processing.
|
|||
/// Use this instead of direct <see cref="Parallel"/> calls!
|
|||
/// </summary>
|
|||
internal static class ParallelHelper |
|||
{ |
|||
/// <summary>
|
|||
/// Get the default <see cref="ParallelExecutionSettings"/> for a <see cref="Configuration"/>
|
|||
/// </summary>
|
|||
public static ParallelExecutionSettings GetParallelSettings(this Configuration configuration) |
|||
{ |
|||
return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/>
|
|||
/// </summary>
|
|||
public static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows) |
|||
where T : struct |
|||
{ |
|||
return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
|
|||
/// </summary>
|
|||
public static void IterateRows(Rectangle rectangle, Configuration configuration, Action<RowInterval> body) |
|||
{ |
|||
ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings(); |
|||
|
|||
IterateRows(rectangle, parallelSettings, body); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
|
|||
/// </summary>
|
|||
public static void IterateRows(Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action<RowInterval> body) |
|||
{ |
|||
int maxSteps = DivideCeil(rectangle.Width * rectangle.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); |
|||
body(rows); |
|||
return; |
|||
} |
|||
|
|||
int verticalStep = DivideRound(rectangle.Height, numOfSteps); |
|||
|
|||
var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps }; |
|||
|
|||
Parallel.For( |
|||
0, |
|||
numOfSteps, |
|||
parallelOptions, |
|||
i => |
|||
{ |
|||
int yMin = rectangle.Top + (i * verticalStep); |
|||
int yMax = Math.Min(yMin + verticalStep, rectangle.Bottom); |
|||
|
|||
var rowInterval = new RowInterval(yMin, yMax); |
|||
body(rowInterval); |
|||
}); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static int DivideCeil(int dividend, int divisor) |
|||
{ |
|||
// TODO: Is there a more efficient way to calculate this?
|
|||
int result = dividend / divisor; |
|||
return dividend % divisor == 0 ? result : result + 1; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static int DivideRound(int dividend, int divisor) |
|||
{ |
|||
return (dividend + (divisor / 2)) / divisor; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// Copyright(c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.ParallelUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an interval of rows in a <see cref="Rectangle"/> and/or <see cref="Buffer2D{T}"/>
|
|||
/// </summary>
|
|||
internal readonly struct RowInterval |
|||
{ |
|||
public RowInterval(int min, int max) |
|||
{ |
|||
this.Min = min; |
|||
this.Max = max; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the INCLUSIVE minimum
|
|||
/// </summary>
|
|||
public int Min { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the EXCLUSIVE maximum
|
|||
/// </summary>
|
|||
public int Max { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the difference (<see cref="Max"/> - <see cref="Min"/>)
|
|||
/// </summary>
|
|||
public int Height => this.Max - this.Min; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common/@EntryIndexedValue">True</s:Boolean> |
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cexceptions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
|||
Loading…
Reference in new issue