Browse Source

optimized BufferPointer.Copy() implementations

af/merge-core
Anton Firszov 9 years ago
parent
commit
ef34b11bca
  1. 14
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 45
      src/ImageSharp/Common/Memory/BufferPointer.cs
  3. 17
      src/ImageSharp/Common/Memory/BufferPointer{T}.cs
  4. 12
      src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
  5. 3
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
  6. 2
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  7. 136
      tests/ImageSharp.Tests/Common/BufferPointerTests.cs

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

@ -59,8 +59,8 @@ namespace ImageSharp
);
Vector<float> bVec = new Vector<float>(256.0f / 255.0f);
Vector<uint> magicInt = new Vector<uint>(1191182336);
Vector<float> magicFloat = new Vector<float>(32768.0f);
Vector<uint> magicInt = new Vector<uint>(1191182336); // reinterpreded value of 32768.0f
Vector<uint> mask = new Vector<uint>(255);
int rawInputSize = count * 4;
@ -92,17 +92,7 @@ namespace ImageSharp
vf.CopyTo(fTemp, i);
}
// TODO: Replace this with an optimized ArrayPointer.Copy() implementation:
uint byteCount = (uint)rawInputSize * sizeof(float);
if (byteCount > 1024u)
{
Marshal.Copy(fTemp, 0, destVectors.PointerAtOffset, rawInputSize);
}
else
{
Unsafe.CopyBlock((void*)destVectors, tPtr, byteCount);
}
BufferPointer.Copy<uint>(tempBuf, (BufferPointer<byte>) destVectors, rawInputSize);
}
}

45
src/ImageSharp/Common/Memory/BufferPointer.cs

@ -6,13 +6,17 @@
namespace ImageSharp
{
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Utility methods for <see cref="BufferPointer{T}"/>
/// </summary>
internal static class BufferPointer
{
/// <summary>
/// It's worth to use Marshal.Copy() over this size.
/// </summary>
private const uint ByteCountThreshold = 1024u;
/// <summary>
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
@ -25,7 +29,19 @@ namespace ImageSharp
public static unsafe void Copy<T>(BufferPointer<T> source, BufferPointer<T> destination, int count)
where T : struct
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, USizeOf<T>(count));
int elementSize = Unsafe.SizeOf<T>();
uint byteCount = (uint) (count * elementSize);
if (byteCount > ByteCountThreshold && elementSize == sizeof(int))
{
// TODO: Add the optimized path for non int-compatible types
int[] srcArray = Unsafe.As<int[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
}
else
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
}
}
/// <summary>
@ -39,7 +55,19 @@ namespace ImageSharp
public static unsafe void Copy<T>(BufferPointer<T> source, BufferPointer<byte> destination, int countInSource)
where T : struct
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, USizeOf<T>(countInSource));
int elementSize = Unsafe.SizeOf<T>();
uint byteCount = (uint)(countInSource * elementSize);
if (byteCount > ByteCountThreshold && elementSize == sizeof(int))
{
// TODO: Add the optimized path for non int-compatible types
int[] srcArray = Unsafe.As<int[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, countInSource);
}
else
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
}
}
/// <summary>
@ -53,7 +81,16 @@ namespace ImageSharp
public static unsafe void Copy<T>(BufferPointer<byte> source, BufferPointer<T> destination, int countInDest)
where T : struct
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, USizeOf<T>(countInDest));
int byteCount = SizeOf<T>(countInDest);
if (byteCount > (int)ByteCountThreshold)
{
Marshal.Copy(source.Array, source.Offset, destination.PointerAtOffset, byteCount);
}
else
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount);
}
}
/// <summary>

17
src/ImageSharp/Common/Memory/BufferPointer{T}.cs

@ -87,7 +87,22 @@ namespace ImageSharp
{
return (byte*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to <see cref="BufferPointer{Byte}"/>
/// setting it's <see cref="Offset"/> and <see cref="PointerAtOffset"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferPointer<byte>(BufferPointer<T> source)
{
BufferPointer<byte> result = default(BufferPointer<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Offset = source.Offset * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferPointer, beginning at 'offset'.
/// </summary>

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

@ -117,6 +117,18 @@ namespace ImageSharp
return new BufferPointer<T>(this.Array, (void*)this.Pointer);
}
/// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to the beginning of the raw data in 'buffer'.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferPointer{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice(int offset)
{
return new BufferPointer<T>(this.Array, (void*)this.Pointer, offset);
}
/// <summary>
/// Disposes the <see cref="PinnedBuffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
/// </summary>

3
tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

@ -212,6 +212,9 @@
<Compile Include="..\ImageSharp.Tests\Colors\BulkPixelOperationsTests.cs">
<Link>Tests\Colors\BulkPixelOperationsTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Common\BufferPointerTests.cs">
<Link>Tests\Common\BufferPointerTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Common\PinnedBufferTests.cs">
<Link>Tests\Common\PinnedBufferTests.cs</Link>
</Compile>

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

@ -17,7 +17,7 @@
{
Assert.IsType<ImageSharp.Color.BulkOperations>(BulkPixelOperations<ImageSharp.Color>.Instance);
}
[Fact]
public void ToVector4SimdAligned()
{

136
tests/ImageSharp.Tests/Common/BufferPointerTests.cs

@ -26,11 +26,26 @@ namespace ImageSharp.Tests.Common
Foo[] result = new Foo[size];
for (int i = 0; i < size; i++)
{
result[i] = new Foo(i, i);
result[i] = new Foo(i+1, i+1);
}
return result;
}
}
[Fact]
public void AsBytes()
{
Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) };
using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz))
{
BufferPointer<Foo> orig = colorBuf.Slice(1);
BufferPointer<byte> asBytes = (BufferPointer < byte > )orig;
Assert.Equal(asBytes.Offset, sizeof(Foo));
Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset);
}
}
[Fact]
public void ConstructWithoutOffset()
@ -93,6 +108,27 @@ namespace ImageSharp.Tests.Common
Assert.NotEqual(default(T), data[idx]);
}
private static byte[] CreateTestBytes(int count)
{
byte[] result = new byte[count];
for (int i = 0; i < result.Length; i++)
{
result[i] = (byte)((i % 200) + 1);
}
return result;
}
private static int[] CreateTestInts(int count)
{
int[] result = new int[count];
for (int i = 0; i < result.Length; i++)
{
result[i] = i + 1;
}
return result;
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
@ -104,56 +140,102 @@ namespace ImageSharp.Tests.Common
fixed (Foo* pSource = source)
fixed (Foo* pDest = dest)
{
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource);
BufferPointer<Foo> apDest = new BufferPointer<Foo>(dest, pDest);
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource, 1);
BufferPointer<Foo> apDest = new BufferPointer<Foo>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count);
BufferPointer.Copy(apSource, apDest, count-1);
}
AssertNotDefault(source, 1);
AssertNotDefault(dest, 1);
Assert.Equal(source[0], dest[0]);
Assert.NotEqual(source[0], dest[0]);
Assert.Equal(source[1], dest[1]);
Assert.Equal(source[2], dest[2]);
Assert.Equal(source[count-1], dest[count-1]);
Assert.NotEqual(source[count], dest[count]);
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
public void IntToInt(int count)
{
int[] source = CreateTestInts(count+2);
int[] dest = new int[count + 5];
fixed (int* pSource = source)
fixed (int* pDest = dest)
{
BufferPointer<int> apSource = new BufferPointer<int>(source, pSource, 1);
BufferPointer<int> apDest = new BufferPointer<int>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count -1);
}
AssertNotDefault(source, 1);
AssertNotDefault(dest, 1);
Assert.NotEqual(source[0], dest[0]);
Assert.Equal(source[1], dest[1]);
Assert.Equal(source[2], dest[2]);
Assert.Equal(source[count - 1], dest[count - 1]);
Assert.NotEqual(source[count], dest[count]);
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
public void GenericToBytes(int count)
{
int destCount = count * sizeof(Foo);
Foo[] source = Foo.CreateArray(count + 2);
byte[] dest = new byte[destCount + sizeof(Foo) + 1];
Foo[] source = Foo.CreateArray(count+2);
byte[] dest = new byte[destCount + sizeof(Foo)*2];
fixed (Foo* pSource = source)
fixed (byte* pDest = dest)
{
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest);
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource, 1);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest, sizeof(Foo));
BufferPointer.Copy(apSource, apDest, count);
BufferPointer.Copy(apSource, apDest, count - 1);
}
AssertNotDefault(source, 1);
Assert.True(ElementsAreEqual(source, dest, 0));
Assert.False(ElementsAreEqual(source, dest, 0));
Assert.True(ElementsAreEqual(source, dest, 1));
Assert.True(ElementsAreEqual(source, dest, 2));
Assert.True(ElementsAreEqual(source, dest, count - 1));
Assert.False(ElementsAreEqual(source, dest, count));
}
private static byte[] CreateTestBytes(int count)
[Theory]
[InlineData(4)]
[InlineData(1500)]
public void IntToBytes(int count)
{
byte[] result = new byte[count];
for (int i = 0; i < result.Length; i++)
int destCount = count * sizeof(int);
int[] source = CreateTestInts(count+2);
byte[] dest = new byte[destCount + sizeof(int) + 1];
fixed (int* pSource = source)
fixed (byte* pDest = dest)
{
result[i] = (byte)(i % 255);
BufferPointer<int> apSource = new BufferPointer<int>(source, pSource);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest);
BufferPointer.Copy(apSource, apDest, count);
}
return result;
AssertNotDefault(source, 1);
Assert.True(ElementsAreEqual(source, dest, 0));
Assert.True(ElementsAreEqual(source, dest, count - 1));
Assert.False(ElementsAreEqual(source, dest, count));
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
@ -199,8 +281,8 @@ namespace ImageSharp.Tests.Common
}
}
}
private static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index)
internal static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index)
{
fixed (Foo* pArray = array)
fixed (byte* pRaw = rawArray)
@ -213,6 +295,20 @@ namespace ImageSharp.Tests.Common
return val1.Equals(val2);
}
}
internal static bool ElementsAreEqual(int[] array, byte[] rawArray, int index)
{
fixed (int* pArray = array)
fixed (byte* pRaw = rawArray)
{
int* pCasted = (int*)pRaw;
int val1 = pArray[index];
int val2 = pCasted[index];
return val1.Equals(val2);
}
}
}
}
}
Loading…
Cancel
Save