Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 2b283f3

Browse files
committed
Optimize Array.Clear using SpanHelpers (#18101)
Reimplement most of Array.Clear in managed code using Span Clear helpers. Fixes dotnet/corefx#29848
1 parent 9c6b90e commit 2b283f3

File tree

4 files changed

+41
-36
lines changed

4 files changed

+41
-36
lines changed

src/classlibnative/bcltype/arraynative.cpp

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,48 +1070,25 @@ FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, Arra
10701070
FCIMPLEND
10711071

10721072

1073-
FCIMPL3(void, ArrayNative::ArrayClear, ArrayBase* pArrayUNSAFE, INT32 iIndex, INT32 iLength)
1073+
FCIMPL5(void*, ArrayNative::GetRawArrayGeometry, ArrayBase* pArray, UINT32* pNumComponents, UINT32* pElementSize, INT32* pLowerBound, CLR_BOOL* pContainsGCPointers)
10741074
{
1075-
FCALL_CONTRACT;
1076-
1077-
BASEARRAYREF pArray = (BASEARRAYREF)pArrayUNSAFE;
1078-
1079-
HELPER_METHOD_FRAME_BEGIN_1(pArray);
1080-
1081-
// cannot pass null for array
1082-
if (pArray == NULL)
1083-
COMPlusThrowArgumentNull(W("array"), W("ArgumentNull_Array"));
1084-
1085-
// array must be an array
1086-
_ASSERTE(pArray->GetMethodTable()->IsArray());
1087-
1088-
// array bounds checking
1089-
int lb = pArray->GetLowerBoundsPtr()[0];
1090-
if (iIndex < lb || (iIndex - lb) < 0 || iLength < 0)
1091-
COMPlusThrow(kIndexOutOfRangeException);
1075+
VALIDATEOBJECT(pArray);
10921076

1093-
if ((iIndex - lb) > (int)pArray->GetNumComponents() - iLength)
1094-
COMPlusThrow(kIndexOutOfRangeException);
1077+
_ASSERTE(pArray != NULL);
10951078

1096-
if (iLength > 0)
1097-
{
1098-
char* array = (char*)pArray->GetDataPtr();
1099-
1100-
SIZE_T size = pArray->GetComponentSize();
1101-
_ASSERTE(size >= 1);
1079+
MethodTable *pMT = pArray->GetMethodTable();
11021080

1103-
ZeroMemoryInGCHeap(array + (iIndex - lb) * size, iLength * size);
1104-
}
1081+
*pNumComponents = pArray->GetNumComponents();
1082+
*pElementSize = pMT->RawGetComponentSize();
1083+
*pLowerBound = pArray->GetLowerBoundsPtr()[0];
1084+
*pContainsGCPointers = !!pMT->ContainsPointers();
11051085

1106-
HELPER_METHOD_FRAME_END();
1086+
return (BYTE*)pArray + ArrayBase::GetDataPtrOffset(pMT);
11071087
}
11081088
FCIMPLEND
11091089

11101090

11111091

1112-
1113-
1114-
11151092
// Check we're allowed to create an array with the given element type.
11161093
void ArrayNative::CheckElementType(TypeHandle elementType)
11171094
{

src/classlibnative/bcltype/arraynative.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class ArrayNative
3535
static FCDECL1(void, Initialize, ArrayBase* pArray);
3636

3737
static FCDECL6(void, ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable);
38-
static FCDECL3(void, ArrayClear, ArrayBase* pArrayUNSAFE, INT32 iIndex, INT32 iLength);
38+
39+
static FCDECL5(void*, GetRawArrayGeometry, ArrayBase* pArray, UINT32* pNumComponents, UINT32* pElementSize, INT32* pLowerBound, CLR_BOOL* pContainsGCPointers);
3940

4041
// This method will create a new array of type type, with zero lower
4142
// bounds and rank.

src/mscorlib/src/System/Array.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
using System.Security;
2222
using Internal.Runtime.CompilerServices;
2323

24+
#if BIT64
25+
using nuint = System.UInt64;
26+
#else
27+
using nuint = System.UInt32;
28+
#endif
29+
2430
namespace System
2531
{
2632
// Note that we make a T[] (single-dimensional w/ zero as the lower bound) implement both
@@ -265,8 +271,29 @@ public static void Copy(Array sourceArray, long sourceIndex, Array destinationAr
265271
// Sets length elements in array to 0 (or null for Object arrays), starting
266272
// at index.
267273
//
268-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
269-
public static extern void Clear(Array array, int index, int length);
274+
public static unsafe void Clear(Array array, int index, int length)
275+
{
276+
if (array == null)
277+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
278+
279+
ref byte p = ref GetRawArrayGeometry(array, out uint numComponents, out uint elementSize, out int lowerBound, out bool containsGCPointers);
280+
281+
int offset = index - lowerBound;
282+
283+
if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > numComponents)
284+
ThrowHelper.ThrowIndexOutOfRangeException();
285+
286+
ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * (nuint)elementSize);
287+
nuint byteLength = (uint)length * (nuint)elementSize;
288+
289+
if (containsGCPointers)
290+
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref ptr), byteLength / (uint)sizeof(IntPtr));
291+
else
292+
SpanHelpers.ClearWithoutReferences(ref ptr, byteLength);
293+
}
294+
295+
[MethodImpl(MethodImplOptions.InternalCall)]
296+
private static extern ref byte GetRawArrayGeometry(Array array, out uint numComponents, out uint elementSize, out int lowerBound, out bool containsGCPointers);
270297

271298
// The various Get values...
272299
public unsafe Object GetValue(params int[] indices)

src/vm/ecalllist.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ FCFuncStart(gArrayFuncs)
810810
FCFuncElement("GetDataPtrOffsetInternal", ArrayNative::GetDataPtrOffsetInternal)
811811
FCFuncElement("Initialize", ArrayNative::Initialize)
812812
FCFuncElement("Copy", ArrayNative::ArrayCopy)
813-
FCFuncElement("Clear", ArrayNative::ArrayClear)
813+
FCFuncElement("GetRawArrayGeometry", ArrayNative::GetRawArrayGeometry)
814814
FCFuncElement("InternalCreate", ArrayNative::CreateInstance)
815815
FCFuncElement("InternalGetReference", ArrayNative::GetReference)
816816
FCFuncElement("InternalSetValue", ArrayNative::SetValue)

0 commit comments

Comments
 (0)