Browse Source

ParallelHelper.IterateRows WIP

pull/710/head
Anton Firszov 8 years ago
parent
commit
c094e12ba7
  1. 53
      src/ImageSharp/Common/Helpers/ParallelFor.cs
  2. 21
      src/ImageSharp/Configuration.cs
  3. 11
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  4. 83
      tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs

53
src/ImageSharp/Common/Helpers/ParallelFor.cs

@ -1,10 +1,63 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
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; }
}
internal static class ParallelHelper
{
public static void IterateRows(in Rectangle rectangle, Configuration configuration, Action<RowInterval> body)
{
int maxSteps = (int)Math.Ceiling(
(float)(rectangle.Width * rectangle.Height) / configuration.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(configuration.MaxDegreeOfParallelism, maxSteps);
int step = rectangle.Height / numOfSteps;
var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps };
int bottom = rectangle.Bottom;
Parallel.For(
0,
numOfSteps,
parallelOptions,
i =>
{
int yMin = i * step;
int yMax = Math.Min(yMin + step, bottom);
var rowInterval = new RowInterval(yMin, yMax);
body(rowInterval);
});
}
}
/// <summary>
/// Utility methods for Parallel.For() execution. Use this instead of raw <see cref="Parallel"/> calls!
/// </summary>

21
src/ImageSharp/Configuration.cs

@ -29,6 +29,8 @@ namespace SixLabors.ImageSharp
private int maxDegreeOfParallelism = Environment.ProcessorCount;
private int minimumPixelsPerTask = 2048;
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
@ -59,6 +61,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms
/// configured with this <see cref="Configuration"/> instance.
/// Initialized with <see cref="Environment.ProcessorCount"/> by default.
/// </summary>
public int MaxDegreeOfParallelism
{
@ -74,6 +77,24 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Gets or sets the minimum number of pixels being processed by a single task when parallelizing operations with TPL.
/// It's not worth to launch tasks processing pixels below this limit. Initialized with 2048 by default.
/// </summary>
public int MinimumPixelsProcessedPerTask
{
get => this.minimumPixelsPerTask;
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(this.MinimumPixelsProcessedPerTask));
}
this.minimumPixelsPerTask = value;
}
}
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>

11
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -96,15 +96,14 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, Rectangle rectangle)
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle)
where T : struct => new BufferArea<T>(buffer, rectangle);
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct
{
var rectangle = new Rectangle(x, y, width, height);
return new BufferArea<T>(buffer, rectangle);
}
where T : struct => new BufferArea<T>(buffer, new Rectangle(x, y, width, height));
public static BufferArea<T> GetAreaBetweenRows<T>(this Buffer2D<T> buffer, int minY, int maxY)
where T : struct => new BufferArea<T>(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY));
/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the whole area of 'buffer'

83
tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs

@ -0,0 +1,83 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Threading;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Helpers
{
public class ParallelHelperTests
{
[Theory]
[InlineData(1, 0, 100, -1, 100)]
[InlineData(2, 0, 9, 5, 4)]
[InlineData(2, 10, 19, 5, 4)]
[InlineData(4, 0, 200, 50, 50)]
[InlineData(4, 123, 323, 50, 50)]
public void IterateRows_OverMinimumPixelsLimit(
int maxDegreeOfParallelism,
int minY,
int maxY,
int expectedStep,
int expectedLastStep)
{
Configuration cfg = Configuration.Default.ShallowCopy();
cfg.MinimumPixelsProcessedPerTask = 1;
cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism;
var rectangle = new Rectangle(0, minY, 10, maxY);
int actualNumberOfSteps = 0;
ParallelHelper.IterateRows(rectangle, cfg,
rows =>
{
Assert.True(rows.Min >= minY);
int step = rows.Max - rows.Min;
int expected = rows.Max < maxY ? expectedStep : expectedLastStep;
Interlocked.Increment(ref actualNumberOfSteps);
Assert.Equal(expected, step);
});
Assert.Equal(maxDegreeOfParallelism, actualNumberOfSteps);
}
[Theory]
[InlineData(2, 200, 50, 2, 1, -1, 2)]
[InlineData(2, 200, 200, 1, 1, -1, 1)]
[InlineData(4, 200, 100, 4, 2, 1, 1)]
[InlineData(4, 300, 100, 8, 3, 3, 2)]
public void IterateRows_WithEffectiveMinimumPixelsLimit(
int maxDegreeOfParallelism,
int minimumPixelsProcessedPerTask,
int width,
int height,
int expectedNumberOfSteps,
int expectedStep,
int expectedLastStep)
{
Configuration cfg = Configuration.Default.ShallowCopy();
cfg.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask;
cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism;
var rectangle = new Rectangle(0, 0, width, height);
int actualNumberOfSteps = 0;
ParallelHelper.IterateRows(rectangle, cfg,
rows =>
{
int step = rows.Max - rows.Min;
int expected = rows.Max < height ? expectedStep : expectedLastStep;
Interlocked.Increment(ref actualNumberOfSteps);
Assert.Equal(expected, step);
});
Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
}
}
}
Loading…
Cancel
Save