diff options
author | Levi Broderick <GrabYourPitchforks@users.noreply.github.com> | 2018-11-02 19:15:43 -0700 |
---|---|---|
committer | Ahson Khan <ahkha@microsoft.com> | 2018-11-02 19:15:43 -0700 |
commit | e8438d33b28e32fa7eb879aa70181087b277f29a (patch) | |
tree | f952a7836b01d132c8e7247a218b2352958e0b2d | |
parent | faa4c87ff3e025c9bed978522f3fe4c85ceecb60 (diff) | |
download | coreclr-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
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); } |