Skip to content

Commit

Permalink
Improve span copy of pointers and structs containing pointers
Browse files Browse the repository at this point in the history
Fixes #9161

PR dotnet#9786 fixes perf of span copy of types that don't contain references
  • Loading branch information
kouvel committed Mar 8, 2017
1 parent afa001d commit 75cc69f
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 16 deletions.
24 changes: 23 additions & 1 deletion src/mscorlib/src/System/Runtime/RuntimeImports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class RuntimeImports
{
// Non-inlinable wrapper around the QCall that avoids poluting the fast path
// with P/Invoke prolog/epilog.
[MethodImplAttribute(MethodImplOptions.NoInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
internal unsafe static void RhZeroMemory(ref byte b, nuint byteLength)
{
fixed (byte* bytePointer = &b)
Expand All @@ -28,5 +28,27 @@ internal unsafe static void RhZeroMemory(ref byte b, nuint byteLength)

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
extern private unsafe static void RhZeroMemory(byte* b, nuint byteLength);

// Non-inlinable wrapper around the QCall that avoids poluting the fast path
// with P/Invoke prolog/epilog.
[MethodImpl(MethodImplOptions.NoInlining)]
internal unsafe static bool RhCopyMemoryWithReferences<T>(ref T destination, ref T source, int elementCount)
{
fixed (void* destinationPtr = &Unsafe.As<T, byte>(ref destination))
{
fixed (void* sourcePtr = &Unsafe.As<T, byte>(ref source))
{
return
RhCopyMemoryWithReferences(
destinationPtr,
sourcePtr,
(nuint)elementCount * (nuint)Unsafe.SizeOf<T>());
}
}
}

[DllImport(JitHelpers.QCall)]
[return: MarshalAs(UnmanagedType.I1)]
extern private unsafe static bool RhCopyMemoryWithReferences(void* destination, void* source, nuint byteCount);
}
}
16 changes: 9 additions & 7 deletions src/mscorlib/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,11 @@ public void Clear()
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
SpanHelper.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)(_length * (Unsafe.SizeOf<T>() / sizeof(nuint))));
SpanHelper.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
}
else
{
SpanHelper.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)(_length * Unsafe.SizeOf<T>()));
SpanHelper.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf<T>());
}
}

Expand Down Expand Up @@ -581,16 +581,18 @@ internal static unsafe void CopyTo<T>(ref T destination, ref T source, int eleme
{
fixed (byte* pSource = &Unsafe.As<T, byte>(ref source))
{
#if BIT64
Buffer.Memmove(pDestination, pSource, (ulong)elementsCount * (ulong)Unsafe.SizeOf<T>());
#else
Buffer.Memmove(pDestination, pSource, (uint)elementsCount * (uint)Unsafe.SizeOf<T>());
#endif
Buffer.Memmove(pDestination, pSource, (nuint)elementsCount * (nuint)Unsafe.SizeOf<T>());
}
}
}
else
{
if ((nuint)elementsCount * (nuint)Unsafe.SizeOf<T>() >= 128 &&
RuntimeImports.RhCopyMemoryWithReferences(ref destination, ref source, elementsCount))
{
return;
}

if (JitHelpers.ByRefLessThan(ref destination, ref source)) // copy forward
{
for (int i = 0; i < elementsCount; i++)
Expand Down
15 changes: 14 additions & 1 deletion src/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1494,13 +1494,26 @@ FCIMPL5(VOID, Buffer::InternalBlockCopy, ArrayBase *src, int srcOffset, ArrayBas
}
FCIMPLEND

void QCALLTYPE SpanNative::SpanClear(void *dst, size_t length)
void QCALLTYPE MemoryNative::Clear(void *dst, size_t length)
{
QCALL_CONTRACT;

memset(dst, 0, length);
}

bool QCALLTYPE MemoryNative::CopyWithReferences(void *dst, void *src, size_t byteCount)
{
QCALL_CONTRACT;

if (!IS_ALIGNED(dst, sizeof(dst)) || !IS_ALIGNED(src, sizeof(src)))
{
return false;
}

memmoveGCRefs(dst, src, byteCount);
return true;
}

void QCALLTYPE Buffer::MemMove(void *dst, void *src, size_t length)
{
QCALL_CONTRACT;
Expand Down
10 changes: 4 additions & 6 deletions src/vm/comutilnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,11 @@ class ExceptionNative
static FCDECL0(INT32, GetExceptionCode);
};


//
// SpanNative
//
class SpanNative {
class MemoryNative
{
public:
static void QCALLTYPE SpanClear(void *dst, size_t length);
static void QCALLTYPE Clear(void *dst, size_t length);
static bool QCALLTYPE CopyWithReferences(void *dst, void *src, size_t byteCount);
};

//
Expand Down
3 changes: 2 additions & 1 deletion src/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -1287,7 +1287,8 @@ FCFuncEnd()
#endif // ifdef FEATURE_COMINTEROP

FCFuncStart(gRuntimeImportsFuncs)
QCFuncElement("RhZeroMemory", SpanNative::SpanClear)
QCFuncElement("RhZeroMemory", MemoryNative::Clear)
QCFuncElement("RhCopyMemoryWithReferences", MemoryNative::CopyWithReferences)
FCFuncEnd()

FCFuncStart(gWeakReferenceFuncs)
Expand Down

0 comments on commit 75cc69f

Please sign in to comment.