Browse Source

fixed ParallelHelper + improved tests

pull/710/head
Anton Firszov 8 years ago
parent
commit
00992043bb
  1. 6
      src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
  2. 6
      src/ImageSharp/Memory/RowInterval.cs
  3. 111
      tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs
  4. 5
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

6
src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs

@ -44,6 +44,8 @@ namespace SixLabors.ImageSharp.ParallelUtils
in ParallelExecutionSettings parallelSettings,
Action<RowInterval> body)
{
DebugGuard.MustBeGreaterThan(rectangle.Width, 0, nameof(rectangle));
int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
@ -56,7 +58,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
return;
}
int verticalStep = DivideRound(rectangle.Height, numOfSteps);
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps };
@ -102,7 +104,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
return;
}
int verticalStep = DivideRound(rectangle.Height, numOfSteps);
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps };

6
src/ImageSharp/Memory/RowInterval.cs

@ -32,5 +32,11 @@ namespace SixLabors.ImageSharp.Memory
/// Gets the difference (<see cref="Max"/> - <see cref="Min"/>)
/// </summary>
public int Height => this.Max - this.Min;
/// <inheritdoc />
public override string ToString()
{
return $"RowInterval [{this.Min}->{this.Max}[";
}
}
}

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

@ -13,11 +13,19 @@ using SixLabors.Memory;
using SixLabors.Primitives;
using Xunit;
using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.Helpers
{
public class ParallelHelperTests
{
private readonly ITestOutputHelper Output;
public ParallelHelperTests(ITestOutputHelper output)
{
this.Output = output;
}
/// <summary>
/// maxDegreeOfParallelism, minY, maxY, expectedStepLength, expectedLastStepLength
/// </summary>
@ -30,12 +38,13 @@ namespace SixLabors.ImageSharp.Tests.Helpers
{ 2, 10, 19, 5, 4 },
{ 4, 0, 200, 50, 50 },
{ 4, 123, 323, 50, 50 },
{ 4, 0, 1201, 300, 301 },
{ 4, 0, 1201, 301, 298 },
{ 8, 10, 236, 29, 23 }
};
[Theory]
[MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))]
public void IterateRows_OverMinimumPixelsLimit(
public void IterateRows_OverMinimumPixelsLimit_IntervalsAreCorrect(
int maxDegreeOfParallelism,
int minY,
int maxY,
@ -50,6 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
var rectangle = new Rectangle(0, minY, 10, maxY - minY);
int actualNumberOfSteps = 0;
ParallelHelper.IterateRows(
rectangle,
parallelSettings,
@ -68,6 +78,40 @@ namespace SixLabors.ImageSharp.Tests.Helpers
Assert.Equal(maxDegreeOfParallelism, actualNumberOfSteps);
}
[Theory]
[MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))]
public void IterateRows_OverMinimumPixelsLimit_ShouldVisitAllRows(
int maxDegreeOfParallelism,
int minY,
int maxY,
int expectedStepLength,
int expectedLastStepLength)
{
var parallelSettings = new ParallelExecutionSettings(
maxDegreeOfParallelism,
1,
Configuration.Default.MemoryAllocator);
var rectangle = new Rectangle(0, minY, 10, maxY - minY);
int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray();
int[] actualData = new int[maxY];
ParallelHelper.IterateRows(
rectangle,
parallelSettings,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
actualData[y] = y;
}
});
Assert.Equal(expectedData, actualData);
}
[Theory]
[MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))]
public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit(
@ -110,6 +154,40 @@ namespace SixLabors.ImageSharp.Tests.Helpers
Assert.Equal(actualNumberOfSteps, numberOfDifferentBuffers);
}
[Theory]
[MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))]
public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit_ShouldVisitAllRows(
int maxDegreeOfParallelism,
int minY,
int maxY,
int expectedStepLength,
int expectedLastStepLength)
{
var parallelSettings = new ParallelExecutionSettings(
maxDegreeOfParallelism,
1,
Configuration.Default.MemoryAllocator);
var rectangle = new Rectangle(0, minY, 10, maxY - minY);
int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray();
int[] actualData = new int[maxY];
ParallelHelper.IterateRowsWithTempBuffer(
rectangle,
parallelSettings,
(RowInterval rows, Memory<Vector4> buffer) =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
actualData[y] = y;
}
});
Assert.Equal(expectedData, actualData);
}
public static TheoryData<int, int, int, int, int, int, int> IterateRows_WithEffectiveMinimumPixelsLimit_Data =
new TheoryData<int, int, int, int, int, int, int>()
{
@ -200,8 +278,11 @@ namespace SixLabors.ImageSharp.Tests.Helpers
public static readonly TheoryData<int, int, int, int, int, int, int> IterateRectangularBuffer_Data =
new TheoryData<int, int, int, int, int, int, int>()
{
{ 8, 582, 453, 10, 10, 291, 226 }, // bounds in DetectEdgesTest.DetectEdges_InBox
{ 8, 582, 453, 10, 10, 291, 226 }, // boundary data from DetectEdgesTest.DetectEdges_InBox
{ 2, 582, 453, 10, 10, 291, 226 },
{ 16, 582, 453, 10, 10, 291, 226 },
{ 16, 582, 453, 10, 10, 1, 226 },
{ 16, 1, 453, 0, 10, 1, 226 },
};
[Theory]
@ -217,33 +298,39 @@ namespace SixLabors.ImageSharp.Tests.Helpers
{
MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator;
using (Buffer2D<int> expected = memoryAllocator.Allocate2D<int>(bufferWidth, bufferHeight, AllocationOptions.Clean))
using (Buffer2D<int> actual = memoryAllocator.Allocate2D<int>(bufferWidth, bufferHeight, AllocationOptions.Clean))
using (Buffer2D<Point> expected = memoryAllocator.Allocate2D<Point>(bufferWidth, bufferHeight, AllocationOptions.Clean))
using (Buffer2D<Point> actual = memoryAllocator.Allocate2D<Point>(bufferWidth, bufferHeight, AllocationOptions.Clean))
{
var rect = new Rectangle(rectX, rectY, rectWidth, rectHeight);
for (int y = rectY; y < rect.Bottom; y++)
void FillRow(int y, Buffer2D<Point> buffer)
{
for (int x = rect.Left; x < rect.Right; x++)
{
expected[x, y] = y * 10000 + x;
buffer[x, y] = new Point(x, y);
}
}
// Fill Expected data:
for (int y = rectY; y < rect.Bottom; y++)
{
FillRow(y, expected);
}
// Fill actual data using IterateRows:
var settings = new ParallelExecutionSettings(maxDegreeOfParallelism, memoryAllocator);
ParallelHelper.IterateRows(rect, settings,
rows =>
{
this.Output.WriteLine(rows.ToString());
for (int y = rows.Min; y < rows.Max; y++)
{
for (int x = rect.Left; x < rect.Right; x++)
{
actual[x, y] = y * 10000 + x;
}
FillRow(y, actual);
}
});
// Assert:
TestImageExtensions.CompareBuffers(expected.Span, actual.Span);
}
}

5
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -453,7 +453,10 @@ namespace SixLabors.ImageSharp.Tests
for (int i = 0; i < expected.Length; i++)
{
Assert.True(expected[i].Equals(actual[i]), $"Buffers differ at position {i}!");
T x = expected[i];
T a = actual[i];
Assert.True(x.Equals(a), $"Buffers differ at position {i}! Expected: {x} | Actual: {a}");
}
}

Loading…
Cancel
Save