Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add internal Array.Clear(Array), optimize some Array callers + codegen #51548

Merged
merged 1 commit into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,28 @@ public static void ConstrainedCopy(Array sourceArray, int sourceIndex, Array des
Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true);
}

internal static unsafe void Clear(Array array)
{
if (array == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be more convenient to just turn this into no-op if the array is null.

ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

MethodTable* pMT = RuntimeHelpers.GetMethodTable(array);
nuint totalByteLength = pMT->ComponentSize * array.NativeLength;
ref byte pStart = ref array.GetRawArrayData();

if (!pMT->ContainsGCPointers)
{
SpanHelpers.ClearWithoutReferences(ref pStart, totalByteLength);
}
else
{
Debug.Assert(totalByteLength % (nuint)sizeof(IntPtr) == 0);
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref pStart), totalByteLength / (nuint)sizeof(IntPtr));
}

// GC.KeepAlive(array) not required. pMT kept alive via `pStart`
}

// Sets length elements in array to 0 (or null for Object arrays), starting
// at index.
//
Expand Down
2 changes: 1 addition & 1 deletion src/libraries/System.Private.CoreLib/src/System/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ bool IList.Contains(object? value)

void IList.Clear()
{
Array.Clear(this, this.GetLowerBound(0), this.Length);
Array.Clear(this);
}

int IList.IndexOf(object? value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public override void Return(T[] array, bool clearArray = false)
// Clear the array if the user requests
if (clearArray)
{
Array.Clear(array, 0, array.Length);
Array.Clear(array);
}

// Return the buffer to its bucket. In the future, we might consider having Return return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public override void Return(T[] array, bool clearArray = false)
// Clear the array if the user requests.
if (clearArray)
{
Array.Clear(array, 0, array.Length);
Array.Clear(array);
}

// Check to see if the buffer is the correct size for this bucket
Expand Down Expand Up @@ -274,7 +274,7 @@ public bool Trim()
foreach (KeyValuePair<T[]?[], object?> tlsBuckets in s_allTlsBuckets)
{
T[]?[] buckets = tlsBuckets.Key;
Array.Clear(buckets, 0, buckets.Length);
Array.Clear(buckets);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public void Clear()
Debug.Assert(_buckets != null, "_buckets should be non-null");
Debug.Assert(_entries != null, "_entries should be non-null");

Array.Clear(_buckets, 0, _buckets.Length);
Array.Clear(_buckets);

_count = 0;
_freeList = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public void Clear()
Debug.Assert(_buckets != null, "_buckets should be non-null");
Debug.Assert(_entries != null, "_entries should be non-null");

Array.Clear(_buckets, 0, _buckets.Length);
Array.Clear(_buckets);
_count = 0;
_freeList = -1;
_freeCount = 0;
Expand Down
37 changes: 30 additions & 7 deletions src/libraries/System.Runtime/tests/System/ArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4655,18 +4655,41 @@ public static void Copy_LargeMultiDimensionalArray()
return;
}

try
short[,] a = AllocateLargeMDArray(2, 2_000_000_000);
a[0, 1] = 42;
Array.Copy(a, 1, a, Int32.MaxValue, 2);
Assert.Equal(42, a[1, Int32.MaxValue - 2_000_000_000]);

Array.Clear(a, Int32.MaxValue - 1, 3);
Assert.Equal(0, a[1, Int32.MaxValue - 2_000_000_000]);
}

[OuterLoop] // Allocates large array
[ConditionalFact]
public static void Clear_LargeMultiDimensionalArray()
{
// If this test is run in a 32-bit process, the large allocation will fail.
if (IntPtr.Size != sizeof(long))
{
short[,] a = new short[2, 2_000_000_000];
a[0, 1] = 42;
Array.Copy(a, 1, a, Int32.MaxValue, 2);
Assert.Equal(42, a[1, Int32.MaxValue - 2_000_000_000]);
return;
}

short[,] a = AllocateLargeMDArray(2, 2_000_000_000);
a[1, 1_999_999_999] = 0x1234;

((IList)a).Clear();
Assert.Equal(0, a[1, 1_999_999_999]);
}

Array.Clear(a, Int32.MaxValue - 1, 3);
Assert.Equal(0, a[1, Int32.MaxValue - 2_000_000_000]);
private static short[,] AllocateLargeMDArray(int dim0Length, int dim1Length)
{
try
{
return new short[dim0Length, dim1Length];
}
catch (OutOfMemoryException)
{
// not a fatal error - we'll just skip the test in this case
throw new SkipTestException("Unable to allocate enough memory");
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/mono/System.Private.CoreLib/src/System/Array.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ public int Rank
get => Rank;
}

internal static unsafe void Clear(Array array)
jkotas marked this conversation as resolved.
Show resolved Hide resolved
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

ref byte ptr = ref array.GetRawSzArrayData();
nuint byteLength = array.NativeLength * (nuint)(uint)array.GetElementSize() /* force zero-extension */;

if (RuntimeHelpers.ObjectHasReferences(array))
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref ptr), byteLength / (uint)sizeof(IntPtr));
else
SpanHelpers.ClearWithoutReferences(ref ptr, byteLength);
}

public static unsafe void Clear(Array array, int index, int length)
{
if (array == null)
Expand Down