Browse Source

better BufferPointer.Copy()

af/merge-core
Anton Firszov 9 years ago
parent
commit
3064b2ba20
  1. 1
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 106
      src/ImageSharp/Common/Memory/BufferPointer.cs
  3. 102
      tests/ImageSharp.Tests/Common/BufferPointerTests.cs

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

@ -66,6 +66,7 @@ namespace ImageSharp
for (; src < srcEnd; src++, dst++)
{
// TODO: We can benefit a lot of future Vector<T> API-s here (https://github.com/dotnet/corefx/issues/15957)
dst->Load(*src);
}

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

@ -5,6 +5,7 @@
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -17,7 +18,7 @@ namespace ImageSharp
/// <summary>
/// It's worth to use Marshal.Copy() over this size.
/// </summary>
private const uint ByteCountThreshold = 1024u;
private const int ByteCountThreshold = 1024;
/// <summary>
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
@ -27,20 +28,10 @@ namespace ImageSharp
/// <param name="destination">The destination <see cref="BufferPointer{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(BufferPointer<T> source, BufferPointer<T> destination, int count)
public static void Copy<T>(BufferPointer<T> source, BufferPointer<T> destination, int count)
where T : struct
{
uint byteCount = USizeOf<T>(count);
if (byteCount > ByteCountThreshold)
{
if (TryMarshalCopy(source, destination, count))
{
return;
}
}
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
CopyImpl(source, destination, count);
}
/// <summary>
@ -51,20 +42,10 @@ namespace ImageSharp
/// <param name="destination">The destination buffer.</param>
/// <param name="countInSource">The number of elements to copy from 'source'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(BufferPointer<T> source, BufferPointer<byte> destination, int countInSource)
public static void Copy<T>(BufferPointer<T> source, BufferPointer<byte> destination, int countInSource)
where T : struct
{
uint byteCount = USizeOf<T>(countInSource);
if (byteCount > ByteCountThreshold)
{
if (TryMarshalCopy(source, destination, countInSource))
{
return;
}
}
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
CopyImpl(source, destination, countInSource);
}
/// <summary>
@ -112,60 +93,37 @@ namespace ImageSharp
=> (uint)SizeOf<T>(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryMarshalCopy<TSource, TDest>(BufferPointer<TSource> source, BufferPointer<TDest> destination, int count)
where TSource : struct
private static unsafe void CopyImpl<T, TDest>(BufferPointer<T> source, BufferPointer<TDest> destination, int count)
where T : struct
where TDest : struct
{
// Pattern Based On:
// https://github.com/dotnet/corefx/blob/master/src/System.Numerics.Vectors/src/System/Numerics/Vector.cs#L12
//
// Note: The following patterns are used throughout the code here and are described here
//
// PATTERN:
// if (typeof(T) == typeof(Int32)) { ... }
// else if (typeof(T) == typeof(Single)) { ... }
// EXPLANATION:
// At runtime, each instantiation of BufferPointer<T> will be type-specific, and each of these typeof blocks will be eliminated,
// as typeof(T) is a (JIT) compile-time constant for each instantiation. This design was chosen to eliminate any overhead from
// delegates and other patterns.
if (typeof(TSource) == typeof(long))
{
long[] srcArray = Unsafe.As<long[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
return true;
}
else if (typeof(TSource) == typeof(int))
{
int[] srcArray = Unsafe.As<int[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
return true;
}
else if (typeof(TSource) == typeof(uint))
{
int[] srcArray = Unsafe.As<int[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
return true;
}
else if (typeof(TSource) == typeof(short))
{
short[] srcArray = Unsafe.As<short[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
return true;
}
else if (typeof(TSource) == typeof(ushort))
{
short[] srcArray = Unsafe.As<short[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
return true;
}
else if (typeof(TSource) == typeof(byte))
int byteCount = SizeOf<T>(count);
if (byteCount > ByteCountThreshold)
{
byte[] srcArray = Unsafe.As<byte[]>(source.Array);
Marshal.Copy(srcArray, source.Offset, destination.PointerAtOffset, count);
return true;
if (Unsafe.SizeOf<T>() == sizeof(long))
{
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
{
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
{
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
return;
}
}
return false;
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount);
}
}
}

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

@ -32,6 +32,37 @@ namespace ImageSharp.Tests.Common
}
}
/// <summary>
/// sizeof(AlignedFoo) == sizeof(long)
/// </summary>
public struct AlignedFoo
{
public int A;
public int B;
static AlignedFoo()
{
Assert.Equal(sizeof(AlignedFoo), sizeof(long));
}
public AlignedFoo(int a, int b)
{
this.A = a;
this.B = b;
}
internal static AlignedFoo[] CreateArray(int size)
{
AlignedFoo[] result = new AlignedFoo[size];
for (int i = 0; i < size; i++)
{
result[i] = new AlignedFoo(i + 1, i + 1);
}
return result;
}
}
[Fact]
public void AsBytes()
{
@ -108,7 +139,6 @@ namespace ImageSharp.Tests.Common
Assert.NotEqual(default(T), data[idx]);
}
private static byte[] CreateTestBytes(int count)
{
byte[] result = new byte[count];
@ -156,6 +186,33 @@ namespace ImageSharp.Tests.Common
Assert.NotEqual(source[count], dest[count]);
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
public void GenericToOwnType_Aligned(int count)
{
AlignedFoo[] source = AlignedFoo.CreateArray(count + 2);
AlignedFoo[] dest = new AlignedFoo[count + 5];
fixed (AlignedFoo* pSource = source)
fixed (AlignedFoo* pDest = dest)
{
BufferPointer<AlignedFoo> apSource = new BufferPointer<AlignedFoo>(source, pSource, 1);
BufferPointer<AlignedFoo> apDest = new BufferPointer<AlignedFoo>(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)]
@ -209,7 +266,34 @@ namespace ImageSharp.Tests.Common
Assert.True(ElementsAreEqual(source, dest, count - 1));
Assert.False(ElementsAreEqual(source, dest, count));
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
public void GenericToBytes_Aligned(int count)
{
int destCount = count * sizeof(Foo);
AlignedFoo[] source = AlignedFoo.CreateArray(count + 2);
byte[] dest = new byte[destCount + sizeof(AlignedFoo) * 2];
fixed (AlignedFoo* pSource = source)
fixed (byte* pDest = dest)
{
BufferPointer<AlignedFoo> apSource = new BufferPointer<AlignedFoo>(source, pSource, 1);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest, sizeof(AlignedFoo));
BufferPointer.Copy(apSource, apDest, count - 1);
}
AssertNotDefault(source, 1);
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));
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
@ -296,6 +380,20 @@ namespace ImageSharp.Tests.Common
}
}
internal static bool ElementsAreEqual(AlignedFoo[] array, byte[] rawArray, int index)
{
fixed (AlignedFoo* pArray = array)
fixed (byte* pRaw = rawArray)
{
AlignedFoo* pCasted = (AlignedFoo*)pRaw;
AlignedFoo val1 = pArray[index];
AlignedFoo val2 = pCasted[index];
return val1.Equals(val2);
}
}
internal static bool ElementsAreEqual(int[] array, byte[] rawArray, int index)
{
fixed (int* pArray = array)

Loading…
Cancel
Save