Browse Source

PixelDataPool<T> is no longer static having PixelDataPool<T>.Clean + PixelDataPool<T>.Dirty instead

af/merge-core
Anton Firszov 9 years ago
parent
commit
973be3c4ac
  1. 4
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 29
      src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
  3. 50
      src/ImageSharp/Common/Memory/PixelDataPool{T}.cs
  4. 4
      src/ImageSharp/Image/ImageBase{TColor}.cs
  5. 61
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  6. 4
      tests/ImageSharp.Benchmarks/Image/EncodePng.cs
  7. 21
      tests/ImageSharp.Sandbox46/Program.cs
  8. 19
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  9. 40
      tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs

4
src/ImageSharp/Colors/Color.BulkOperations.cs

@ -55,7 +55,9 @@ namespace ImageSharp
uint* src = (uint*)sourceColors.PointerAtOffset;
uint* srcEnd = src + count;
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>(unpackedRawCount + Vector<uint>.Count))
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>(
unpackedRawCount + Vector<uint>.Count,
PixelDataPool<uint>.Dirty))
{
uint* tPtr = (uint*)tempBuf.Pointer;
uint[] temp = tempBuf.Array;

29
src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs

@ -24,22 +24,32 @@ namespace ImageSharp
private GCHandle handle;
/// <summary>
/// A value indicating whether this <see cref="PinnedBuffer{T}"/> instance should return the array to the pool.
/// The <see cref="PixelDataPool{T}"/> if the <see cref="Array"/> is pooled.
/// </summary>
private bool isPoolingOwner;
private PixelDataPool<T> pool;
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int count)
/// <param name="pool">The <see cref="PixelDataPool{T}"/> to be used to rent the data.</param>
public PinnedBuffer(int count, PixelDataPool<T> pool)
{
this.Count = count;
this.Array = PixelDataPool<T>.Rent(count);
this.isPoolingOwner = true;
this.pool = pool;
this.Array = this.pool.Rent(count);
this.Pin();
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int count)
: this(count, PixelDataPool<T>.Clean)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
@ -48,6 +58,7 @@ namespace ImageSharp
{
this.Count = array.Length;
this.Array = array;
this.pool = null;
this.Pin();
}
@ -65,6 +76,7 @@ namespace ImageSharp
this.Count = count;
this.Array = array;
this.pool = null;
this.Pin();
}
@ -140,11 +152,8 @@ namespace ImageSharp
this.IsDisposedOrLostArrayOwnership = true;
this.UnPin();
if (this.isPoolingOwner)
{
PixelDataPool<T>.Return(this.Array);
}
this.pool?.Return(this.Array);
this.pool = null;
this.Array = null;
this.Count = 0;

50
src/ImageSharp/Common/Memory/PixelDataPool{T}.cs

@ -9,39 +9,69 @@ namespace ImageSharp
using System.Buffers;
/// <summary>
/// Provides a resource pool that enables reusing instances of value type arrays <see cref="T:T[]"/>.
/// <see cref="Rent(int)"/> will always return arrays initialized with 'default(T)'
/// Provides a resource pool that enables reusing instances of value type arrays for image data <see cref="T:T[]"/>.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
public static class PixelDataPool<T>
public class PixelDataPool<T>
where T : struct
{
/// <summary>
/// The <see cref="ArrayPool{T}"/> used to pool data.
/// The <see cref="ArrayPool{T}"/> which will be always cleared.
/// </summary>
private static readonly ArrayPool<T> ArrayPool = ArrayPool<T>.Create(CalculateMaxArrayLength(), 50);
private static readonly ArrayPool<T> CleanPool = ArrayPool<T>.Create(CalculateMaxArrayLength(), 50);
/// <summary>
/// The <see cref="ArrayPool{T}"/> which is not kept clean.
/// </summary>
private static readonly ArrayPool<T> DirtyPool = ArrayPool<T>.Create(CalculateMaxArrayLength(), 50);
/// <summary>
/// The backing <see cref="ArrayPool{T}"/>
/// </summary>
private ArrayPool<T> arrayPool;
/// <summary>
/// A value indicating whether clearArray is requested on <see cref="ArrayPool{T}.Return(T[], bool)"/>.
/// </summary>
private bool clearArray;
private PixelDataPool(ArrayPool<T> arrayPool, bool clearArray)
{
this.clearArray = clearArray;
this.arrayPool = arrayPool;
}
/// <summary>
/// Gets the <see cref="PixelDataPool{T}"/> which will always return arrays initialized to default(T)
/// </summary>
public static PixelDataPool<T> Clean { get; } = new PixelDataPool<T>(CleanPool, true);
/// <summary>
/// Gets the <see cref="PixelDataPool{T}"/> which does not keep the arrays clean on Rent/Return.
/// </summary>
public static PixelDataPool<T> Dirty { get; } = new PixelDataPool<T>(DirtyPool, false);
/// <summary>
/// Rents the pixel array from the pool.
/// </summary>
/// <param name="minimumLength">The minimum length of the array to return.</param>
/// <returns>The <see cref="T:TColor[]"/></returns>
public static T[] Rent(int minimumLength)
public T[] Rent(int minimumLength)
{
return ArrayPool.Rent(minimumLength);
return CleanPool.Rent(minimumLength);
}
/// <summary>
/// Returns the rented pixel array back to the pool.
/// </summary>
/// <param name="array">The array to return to the buffer pool.</param>
public static void Return(T[] array)
public void Return(T[] array)
{
ArrayPool.Return(array, true);
CleanPool.Return(array, this.clearArray);
}
/// <summary>
/// Heuristically calculates a reasonable maxArrayLength value for the backing <see cref="ArrayPool"/>.
/// Heuristically calculates a reasonable maxArrayLength value for the backing <see cref="CleanPool"/>.
/// </summary>
/// <returns>The maxArrayLength value</returns>
internal static int CalculateMaxArrayLength()

4
src/ImageSharp/Image/ImageBase{TColor}.cs

@ -221,7 +221,7 @@ namespace ImageSharp
/// </summary>
private void RentPixels()
{
this.pixelBuffer = PixelDataPool<TColor>.Rent(this.Width * this.Height);
this.pixelBuffer = PixelDataPool<TColor>.Clean.Rent(this.Width * this.Height);
}
/// <summary>
@ -229,7 +229,7 @@ namespace ImageSharp
/// </summary>
private void ReturnPixels()
{
PixelDataPool<TColor>.Return(this.pixelBuffer);
PixelDataPool<TColor>.Clean.Return(this.pixelBuffer);
this.pixelBuffer = null;
}
}

61
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs

@ -0,0 +1,61 @@
// ReSharper disable InconsistentNaming
namespace ImageSharp.Benchmarks.Color.Bulk
{
using System.Numerics;
using BenchmarkDotNet.Attributes;
public abstract class ToVector4<TColor>
where TColor : struct, IPixel<TColor>
{
private PinnedBuffer<TColor> source;
private PinnedBuffer<Vector4> destination;
[Params(16, 128, 1024)]
public int Count { get; set; }
[Setup]
public void Setup()
{
this.source = new PinnedBuffer<TColor>(this.Count);
this.destination = new PinnedBuffer<Vector4>(this.Count);
}
[Cleanup]
public void Cleanup()
{
this.source.Dispose();
this.destination.Dispose();
}
[Benchmark(Baseline = true)]
public void PerElement()
{
TColor[] s = this.source.Array;
Vector4[] d = this.destination.Array;
for (int i = 0; i < this.Count; i++)
{
TColor c = s[i];
d[i] = c.ToVector4();
}
}
[Benchmark]
public void CommonBulk()
{
new BulkPixelOperations<TColor>().ToVector4(this.source, this.destination, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
BulkPixelOperations<TColor>.Instance.ToVector4(this.source, this.destination, this.Count);
}
}
public class ToVector4_Color : ToVector4<ImageSharp.Color>
{
}
}

4
tests/ImageSharp.Benchmarks/Image/EncodePng.cs

@ -23,10 +23,10 @@ namespace ImageSharp.Benchmarks.Image
private Image bmpDrawing;
private CoreImage bmpCore;
[Params(false, true)]
[Params(false)]
public bool LargeImage { get; set; }
[Params(false, true)]
[Params(false)]
public bool UseOctreeQuantizer { get; set; }
[Setup]

21
tests/ImageSharp.Sandbox46/Program.cs

@ -7,9 +7,9 @@ namespace ImageSharp.Sandbox46
{
using System;
using System.Runtime.DesignerServices;
using ImageSharp.Benchmarks.Color.Bulk;
using ImageSharp.Tests;
using ImageSharp.Tests.Colors;
using Xunit.Abstractions;
@ -38,21 +38,18 @@ namespace ImageSharp.Sandbox46
public static void Main(string[] args)
{
// RunDecodeJpegProfilingTests();
TestPixelAccessorCopyFromXyzw();
RunToVector4ProfilingTest();
Console.ReadLine();
}
private static void TestPixelAccessorCopyFromXyzw()
private static void RunToVector4ProfilingTest()
{
PixelAccessorVirtualCopy benchmark = new PixelAccessorVirtualCopy();
benchmark.Width = 64;
benchmark.Setup();
benchmark.CopyRawUnsafeInlined();
benchmark.Cleanup();
BulkPixelOperationsTests.Color tests = new BulkPixelOperationsTests.Color();
tests.Benchmark_ToVector4();
}
private static void RunDecodeJpegProfilingTests()
{
Console.WriteLine("RunDecodeJpegProfilingTests...");

19
tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs

@ -1,4 +1,5 @@
namespace ImageSharp.Tests.Colors
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Colors
{
using System;
using System.Numerics;
@ -30,6 +31,22 @@
(s, d) => ImageSharp.Color.BulkOperations.ToVector4SimdAligned(s, d, 64)
);
}
[Fact]
public void Benchmark_ToVector4()
{
int times = 150000;
int count = 1024;
using (PinnedBuffer<ImageSharp.Color> source = new PinnedBuffer<ImageSharp.Color>(count))
using (PinnedBuffer<Vector4> dest = new PinnedBuffer<Vector4>(count))
{
for (int i = 0; i < times; i++)
{
BulkPixelOperations<ImageSharp.Color>.Instance.ToVector4(source, dest, count);
}
}
}
}
public class Argb : BulkPixelOperationsTests<ImageSharp.Argb>

40
tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs

@ -3,6 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests
{
using System.Linq;
@ -14,50 +15,59 @@ namespace ImageSharp.Tests
/// </summary>
public class PixelDataPoolTests
{
[Fact]
public void PixelDataPoolRentsMinimumSize()
private static PixelDataPool<Color> GetPool(bool clean)
{
Color[] pixels = PixelDataPool<Color>.Rent(1024);
return clean ? PixelDataPool<Color>.Clean : PixelDataPool<Color>.Dirty;
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void PixelDataPoolRentsMinimumSize(bool clean)
{
Color[] pixels = GetPool(clean).Rent(1024);
Assert.True(pixels.Length >= 1024);
}
[Fact]
public void PixelDataPoolRentsEmptyArray()
public void PixelDataPool_Clean_RentsCleanArray()
{
for (int i = 16; i < 1024; i += 16)
{
Color[] pixels = PixelDataPool<Color>.Rent(i);
Color[] pixels = PixelDataPool<Color>.Clean.Rent(i);
Assert.True(pixels.All(p => p == default(Color)));
PixelDataPool<Color>.Return(pixels);
PixelDataPool<Color>.Clean.Return(pixels);
}
for (int i = 16; i < 1024; i += 16)
{
Color[] pixels = PixelDataPool<Color>.Rent(i);
Color[] pixels = PixelDataPool<Color>.Clean.Rent(i);
Assert.True(pixels.All(p => p == default(Color)));
PixelDataPool<Color>.Return(pixels);
PixelDataPool<Color>.Clean.Return(pixels);
}
}
[Fact]
public void PixelDataPoolDoesNotThrowWhenReturningNonPooled()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void PixelDataPoolDoesNotThrowWhenReturningNonPooled(bool clean)
{
Color[] pixels = new Color[1024];
PixelDataPool<Color>.Return(pixels);
GetPool(clean).Return(pixels);
Assert.True(pixels.Length >= 1024);
}
[Fact]
public void PixelDataPoolCleansRentedArray()
public void PixelDataPool_Clean_CleansRentedArray()
{
Color[] pixels = PixelDataPool<Color>.Rent(256);
Color[] pixels = PixelDataPool<Color>.Clean.Rent(256);
for (int i = 0; i < pixels.Length; i++)
{
@ -66,7 +76,7 @@ namespace ImageSharp.Tests
Assert.True(pixels.All(p => p == Color.Azure));
PixelDataPool<Color>.Return(pixels);
PixelDataPool<Color>.Clean.Return(pixels);
Assert.True(pixels.All(p => p == default(Color)));
}
@ -85,7 +95,7 @@ namespace ImageSharp.Tests
[Fact]
public void RentNonIPixelData()
{
byte[] data = PixelDataPool<byte>.Rent(16384);
byte[] data = PixelDataPool<byte>.Clean.Rent(16384);
Assert.True(data.Length >= 16384);
}

Loading…
Cancel
Save