summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLevi Broderick <GrabYourPitchforks@users.noreply.github.com>2018-11-02 19:15:43 -0700
committerAhson Khan <ahkha@microsoft.com>2018-11-02 19:15:43 -0700
commite8438d33b28e32fa7eb879aa70181087b277f29a (patch)
treef952a7836b01d132c8e7247a218b2352958e0b2d
parentfaa4c87ff3e025c9bed978522f3fe4c85ceecb60 (diff)
downloadcoreclr-e8438d33b28e32fa7eb879aa70181087b277f29a.tar.gz
coreclr-e8438d33b28e32fa7eb879aa70181087b277f29a.tar.bz2
coreclr-e8438d33b28e32fa7eb879aa70181087b277f29a.zip
Perf improvements to Span and Memory (#20771)
* Perf improvements to Span and Memory - Improves perf of AsSpan, AsMemory, ctor, and Slice * PR feedback - add comments
-rw-r--r--src/System.Private.CoreLib/shared/System/Memory.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs12
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs12
-rw-r--r--src/System.Private.CoreLib/shared/System/Span.Fast.cs17
5 files changed, 65 insertions, 4 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Memory.cs b/src/System.Private.CoreLib/shared/System/Memory.cs
index 1a7556720d..e4596d3119 100644
--- a/src/System.Private.CoreLib/shared/System/Memory.cs
+++ b/src/System.Private.CoreLib/shared/System/Memory.cs
@@ -104,8 +104,14 @@ namespace System
}
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
+#endif
_object = array;
_index = start;
@@ -250,10 +256,14 @@ namespace System
// Used to maintain the high-bit which indicates whether the Memory has been pre-pinned or not.
int capturedLength = _length;
int actualLength = capturedLength & RemoveFlagsBitMask;
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)actualLength)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)actualLength || (uint)length > (uint)(actualLength - start))
- {
ThrowHelper.ThrowArgumentOutOfRangeException();
- }
+#endif
// Set the high-bit to match the this._length high bit (1 for pre-pinned, 0 for unpinned).
return new Memory<T>(_object, _index + start, length | (capturedLength & ~RemoveFlagsBitMask));
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
index 31051c86c9..99fb83e635 100644
--- a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
@@ -450,8 +450,14 @@ namespace System
return default;
}
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
}
@@ -506,8 +512,14 @@ namespace System
return default;
}
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
return new ReadOnlyMemory<char>(text, start, length);
}
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
index 02445ec5c6..7a332c1fa3 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
@@ -77,8 +77,14 @@ namespace System
this = default;
return; // returns default
}
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
+#endif
_object = array;
_index = start;
@@ -172,10 +178,14 @@ namespace System
// Used to maintain the high-bit which indicates whether the Memory has been pre-pinned or not.
int capturedLength = _length;
int actualLength = _length & RemoveFlagsBitMask;
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)actualLength)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
if ((uint)start > (uint)actualLength || (uint)length > (uint)(actualLength - start))
- {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
- }
+#endif
// Set the high-bit to match the this._length high bit (1 for pre-pinned, 0 for unpinned).
return new ReadOnlyMemory<T>(_object, _index + start, length | (capturedLength & ~RemoveFlagsBitMask));
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
index 8ba8fd66b6..3077e2f659 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
@@ -75,8 +75,14 @@ namespace System
this = default;
return; // returns default
}
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
+#endif
_pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
_length = length;
@@ -259,8 +265,14 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Slice(int start, int length)
{
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
+#endif
return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
}
diff --git a/src/System.Private.CoreLib/shared/System/Span.Fast.cs b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
index 3073592cd6..626c06e478 100644
--- a/src/System.Private.CoreLib/shared/System/Span.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
@@ -81,8 +81,14 @@ namespace System
}
if (default(T) == null && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
+#endif
_pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
_length = length;
@@ -338,8 +344,19 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> Slice(int start, int length)
{
+#if BIT64
+ // Since start and length are both 32-bit, their sum can be computed across a 64-bit domain
+ // without loss of fidelity. The cast to uint before the cast to ulong ensures that the
+ // extension from 32- to 64-bit is zero-extending rather than sign-extending. The end result
+ // of this is that if either input is negative or if the input sum overflows past Int32.MaxValue,
+ // that information is captured correctly in the comparison against the backing _length field.
+ // We don't use this same mechanism in a 32-bit process due to the overhead of 64-bit arithmetic.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+#else
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
+#endif
return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
}