From c7a2d67c031ea6268307d212fe04f1bdddc9a20f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 11 Jun 2020 02:02:22 +0200 Subject: [PATCH 1/3] Optimized Span enumerables --- .../Enumerables/ReadOnlySpanEnumerable{T}.cs | 90 +++++-------- .../Enumerables/ReadOnlySpanTokenizer{T}.cs | 123 +++++++----------- .../Enumerables/SpanEnumerable{T}.cs | 102 ++++++--------- .../Enumerables/SpanTokenizer{T}.cs | 123 +++++++----------- 4 files changed, 168 insertions(+), 270 deletions(-) diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs index 51b8f675677..29d42924536 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs @@ -15,94 +15,72 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct ReadOnlySpanEnumerable + public ref struct ReadOnlySpanEnumerable { /// - /// The source instance + /// The source instance. /// private readonly ReadOnlySpan span; + /// + /// The current index within . + /// + private int index; + /// /// Initializes a new instance of the struct. /// - /// The source to enumerate. + /// The source instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpanEnumerable(ReadOnlySpan span) { this.span = span; + this.index = -1; } /// /// Implements the duck-typed method. /// - /// An instance targeting the current value. + /// An instance targeting the current value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(this.span); + public ReadOnlySpanEnumerable GetEnumerator() => this; /// - /// An enumerator for a source instance. + /// Implements the duck-typed method. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Enumerator + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() { - /// - /// The source instance. - /// - private readonly ReadOnlySpan span; - - /// - /// The current index within . - /// - private int index; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator(ReadOnlySpan span) - { - this.span = span; - this.index = -1; - } + int newIndex = this.index + 1; - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + if (newIndex < this.span.Length) { - int newIndex = this.index + 1; + this.index = newIndex; - if (newIndex < this.span.Length) - { - this.index = newIndex; - - return true; - } - - return false; + return true; } - /// - /// Gets the duck-typed property. - /// - public Item Current + return false; + } + + /// + /// Gets the duck-typed property. + /// + public Item Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { #if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); - ref T ri = ref Unsafe.Add(ref r0, this.index); + ref T r0 = ref MemoryMarshal.GetReference(this.span); + ref T ri = ref Unsafe.Add(ref r0, this.index); - // See comment in SpanEnumerable about this - return new Item(ref ri, this.index); + // See comment in SpanEnumerable about this + return new Item(ref ri, this.index); #else - return new Item(this.span, this.index); + return new Item(this.span, this.index); #endif - } } } diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs index 5a9c7d13823..3977f5829f8 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs @@ -14,119 +14,90 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct ReadOnlySpanTokenizer + public ref struct ReadOnlySpanTokenizer where T : IEquatable { /// - /// The source instance + /// The source instance. /// private readonly ReadOnlySpan span; /// - /// The separator item to use. + /// The separator item to use. /// private readonly T separator; + /// + /// The current initial offset. + /// + private int start; + + /// + /// The current final offset. + /// + private int end; + /// /// Initializes a new instance of the struct. /// - /// The source to tokenize. - /// The separator item to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// The source instance. + /// The separator item to use. public ReadOnlySpanTokenizer(ReadOnlySpan span, T separator) { this.span = span; this.separator = separator; + this.start = 0; + this.end = -1; } /// /// Implements the duck-typed method. /// - /// An instance targeting the current value. + /// An instance targeting the current value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(this.span, this.separator); + public ReadOnlySpanTokenizer GetEnumerator() => this; /// - /// An enumerator for a source instance. + /// Implements the duck-typed method. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Enumerator + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() { - /// - /// The source instance. - /// - private readonly ReadOnlySpan span; - - /// - /// The separator item to use. - /// - private readonly T separator; - - /// - /// The current initial offset. - /// - private int start; - - /// - /// The current final offset. - /// - private int end; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - /// The separator item to use. - public Enumerator(ReadOnlySpan span, T separator) - { - this.span = span; - this.separator = separator; - this.start = 0; - this.end = -1; - } + int + newEnd = this.end + 1, + length = this.span.Length; - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + // Additional check if the separator is not the last character + if (newEnd <= length) { - int - newEnd = this.end + 1, - length = this.span.Length; - - // Additional check if the separator is not the last character - if (newEnd <= length) - { - this.start = newEnd; + this.start = newEnd; - int index = this.span.Slice(newEnd).IndexOf(this.separator); + int index = this.span.Slice(newEnd).IndexOf(this.separator); - // Extract the current subsequence - if (index >= 0) - { - this.end = newEnd + index; - - return true; - } - - this.end = length; + // Extract the current subsequence + if (index >= 0) + { + this.end = newEnd + index; return true; } - return false; - } + this.end = length; - /// - /// Gets the duck-typed property. - /// - public ReadOnlySpan Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.span.Slice(this.start, this.end - this.start); + return true; } + + return false; + } + + /// + /// Gets the duck-typed property. + /// + public ReadOnlySpan Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.span.Slice(this.start, this.end - this.start); } } } diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs index 1db240cc447..791f509339d 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs @@ -15,99 +15,77 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct SpanEnumerable + public ref struct SpanEnumerable { /// - /// The source instance + /// The source instance. /// private readonly Span span; + /// + /// The current index within . + /// + private int index; + /// /// Initializes a new instance of the struct. /// - /// The source to enumerate. + /// The source instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public SpanEnumerable(Span span) { this.span = span; + this.index = -1; } /// /// Implements the duck-typed method. /// - /// An instance targeting the current value. + /// An instance targeting the current value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(this.span); + public SpanEnumerable GetEnumerator() => this; /// - /// An enumerator for a source instance. + /// Implements the duck-typed method. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Enumerator + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() { - /// - /// The source instance. - /// - private readonly Span span; - - /// - /// The current index within . - /// - private int index; + int newIndex = this.index + 1; - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator(Span span) + if (newIndex < this.span.Length) { - this.span = span; - this.index = -1; - } + this.index = newIndex; - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int newIndex = this.index + 1; - - if (newIndex < this.span.Length) - { - this.index = newIndex; - - return true; - } - - return false; + return true; } - /// - /// Gets the duck-typed property. - /// - public Item Current + return false; + } + + /// + /// Gets the duck-typed property. + /// + public Item Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { #if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); - ref T ri = ref Unsafe.Add(ref r0, this.index); - - // On .NET Standard 2.1 we can save 4 bytes by piggybacking - // the current index in the length of the wrapped span. - // We're going to use the first item as the target reference, - // and the length as a host for the current original offset. - // This is not possible on .NET Standard 2.1 as we lack - // the API to create spans from arbitrary references. - return new Item(ref ri, this.index); + ref T r0 = ref MemoryMarshal.GetReference(this.span); + ref T ri = ref Unsafe.Add(ref r0, this.index); + + // On .NET Standard 2.1 we can save 4 bytes by piggybacking + // the current index in the length of the wrapped span. + // We're going to use the first item as the target reference, + // and the length as a host for the current original offset. + // This is not possible on .NET Standard 2.1 as we lack + // the API to create spans from arbitrary references. + return new Item(ref ri, this.index); #else - return new Item(this.span, this.index); + return new Item(this.span, this.index); #endif - } } } diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs index a3034d67a0a..953eb535f0b 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs @@ -14,119 +14,90 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct SpanTokenizer + public ref struct SpanTokenizer where T : IEquatable { /// - /// The source instance + /// The source instance. /// private readonly Span span; /// - /// The separator item to use. + /// The separator item to use. /// private readonly T separator; + /// + /// The current initial offset. + /// + private int start; + + /// + /// The current final offset. + /// + private int end; + /// /// Initializes a new instance of the struct. /// - /// The source to tokenize. - /// The separator item to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// The source instance. + /// The separator item to use. public SpanTokenizer(Span span, T separator) { this.span = span; this.separator = separator; + this.start = 0; + this.end = -1; } /// /// Implements the duck-typed method. /// - /// An instance targeting the current value. + /// An instance targeting the current value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(this.span, this.separator); + public SpanTokenizer GetEnumerator() => this; /// - /// An enumerator for a source instance. + /// Implements the duck-typed method. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Enumerator + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() { - /// - /// The source instance. - /// - private readonly Span span; - - /// - /// The separator item to use. - /// - private readonly T separator; - - /// - /// The current initial offset. - /// - private int start; - - /// - /// The current final offset. - /// - private int end; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - /// The separator item to use. - public Enumerator(Span span, T separator) - { - this.span = span; - this.separator = separator; - this.start = 0; - this.end = -1; - } + int + newEnd = this.end + 1, + length = this.span.Length; - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + // Additional check if the separator is not the last character + if (newEnd <= length) { - int - newEnd = this.end + 1, - length = this.span.Length; - - // Additional check if the separator is not the last character - if (newEnd <= length) - { - this.start = newEnd; + this.start = newEnd; - int index = this.span.Slice(newEnd).IndexOf(this.separator); + int index = this.span.Slice(newEnd).IndexOf(this.separator); - // Extract the current subsequence - if (index >= 0) - { - this.end = newEnd + index; - - return true; - } - - this.end = length; + // Extract the current subsequence + if (index >= 0) + { + this.end = newEnd + index; return true; } - return false; - } + this.end = length; - /// - /// Gets the duck-typed property. - /// - public Span Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.span.Slice(this.start, this.end - this.start); + return true; } + + return false; + } + + /// + /// Gets the duck-typed property. + /// + public Span Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.span.Slice(this.start, this.end - this.start); } } } From 26768a539282ad64d5ee87c28d175ff3f336d9df Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 11 Jun 2020 02:04:14 +0200 Subject: [PATCH 2/3] Optimized array enumerables --- .../Enumerables/Array2DColumnEnumerable{T}.cs | 227 ++++++++---------- .../Enumerables/Array2DRowEnumerable{T}.cs | 141 +++++------ 2 files changed, 155 insertions(+), 213 deletions(-) diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs index 6cd0ceaf9f3..37d53196e8d 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs @@ -16,10 +16,35 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct Array2DColumnEnumerable + public ref struct Array2DColumnEnumerable { +#if SPAN_RUNTIME_SUPPORT + /// + /// The instance mapping the target 2D array. + /// + /// + /// In runtimes where we have support for the type, we can + /// create one from the input 2D array and use that to traverse the target column. + /// This reduces the number of operations to perform for the offsetting to the right + /// column element (we simply need to add to the offset at each + /// iteration to move down by one row), and allows us to use the fast + /// accessor instead of the slower indexer for 2D arrays, as we can then access each + /// individual item linearly, since we know the absolute offset from the base location. + /// + private readonly Span span; + + /// + /// The width of the target 2D array. + /// + private readonly int width; + + /// + /// The current absolute offset within . + /// + private int offset; +#else /// - /// The source 2D array instance. + /// The source 2D array instance. /// private readonly T[,] array; @@ -28,24 +53,93 @@ public readonly ref struct Array2DColumnEnumerable /// private readonly int column; + /// + /// The height of a column in . + /// + private readonly int height; + + /// + /// The current row. + /// + private int row; +#endif + /// /// Initializes a new instance of the struct. /// - /// The source 2D array instance. + /// The source 2D array instance. /// The target column to iterate within . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Array2DColumnEnumerable(T[,] array, int column) { + if ((uint)column >= (uint)array.GetLength(1)) + { + ThrowArgumentOutOfRangeExceptionForInvalidColumn(); + } + +#if SPAN_RUNTIME_SUPPORT + this.span = array.AsSpan(); + this.width = array.GetLength(1); + this.offset = column - this.width; +#else this.array = array; this.column = column; + this.height = array.GetLength(0); + this.row = -1; +#endif } /// /// Implements the duck-typed method. /// - /// An instance targeting the current 2D array instance. + /// An instance targeting the current 2D array instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(this.array, this.column); + public Array2DColumnEnumerable GetEnumerator() => this; + + /// + /// Implements the duck-typed method. + /// + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { +#if SPAN_RUNTIME_SUPPORT + int offset = this.offset + this.width; + + if ((uint)offset < (uint)this.span.Length) + { + this.offset = offset; + + return true; + } +#else + int row = this.row + 1; + + if (row < this.height) + { + this.row = row; + + return true; + } +#endif + return false; + } + + /// + /// Gets the duck-typed property. + /// + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if SPAN_RUNTIME_SUPPORT + return ref this.span.DangerousGetReferenceAt(this.offset); +#else + return ref this.array[this.row, this.column]; +#endif + } + } /// /// Returns a array with the values in the target column. @@ -79,129 +173,6 @@ public T[] ToArray() return array; } - /// - /// An enumerator for a source 2D array instance. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Enumerator - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance mapping the target 2D array. - /// - /// - /// In runtimes where we have support for the type, we can - /// create one from the input 2D array and use that to traverse the target column. - /// This reduces the number of operations to perform for the offsetting to the right - /// column element (we simply need to add to the offset at each - /// iteration to move down by one row), and allows us to use the fast - /// accessor instead of the slower indexer for 2D arrays, as we can then access each - /// individual item linearly, since we know the absolute offset from the base location. - /// - private readonly Span span; - - /// - /// The width of the target 2D array. - /// - private readonly int width; - - /// - /// The current absolute offset within . - /// - private int offset; -#else - /// - /// The source 2D array instance. - /// - private readonly T[,] array; - - /// - /// The target column to iterate within . - /// - private readonly int column; - - /// - /// The height of a column in . - /// - private readonly int height; - - /// - /// The current row. - /// - private int row; -#endif - - /// - /// Initializes a new instance of the struct. - /// - /// The source 2D array instance. - /// The target column to iterate within . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator(T[,] array, int column) - { - if ((uint)column >= (uint)array.GetLength(1)) - { - ThrowArgumentOutOfRangeExceptionForInvalidColumn(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = array.AsSpan(); - this.width = array.GetLength(1); - this.offset = column - this.width; -#else - this.array = array; - this.column = column; - this.height = array.GetLength(0); - this.row = -1; -#endif - } - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { -#if SPAN_RUNTIME_SUPPORT - int offset = this.offset + this.width; - - if ((uint)offset < (uint)this.span.Length) - { - this.offset = offset; - - return true; - } -#else - int row = this.row + 1; - - if (row < this.height) - { - this.row = row; - - return true; - } -#endif - return false; - } - - /// - /// Gets the duck-typed property. - /// - public ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return ref this.span.DangerousGetReferenceAt(this.offset); -#else - return ref this.array[this.row, this.column]; -#endif - } - } - } - /// /// Throws an when the is invalid. /// diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs index a0d1fe98f5c..7f21bba5f2d 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs @@ -18,10 +18,10 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct Array2DRowEnumerable + public ref struct Array2DRowEnumerable { /// - /// The source 2D array instance. + /// The source 2D array instance. /// private readonly T[,] array; @@ -30,24 +30,75 @@ public readonly ref struct Array2DRowEnumerable /// private readonly int row; + /// + /// The width of a row in . + /// + private readonly int width; + + /// + /// The current column. + /// + private int column; + /// /// Initializes a new instance of the struct. /// - /// The source 2D array instance. + /// The source 2D array instance. /// The target row to iterate within . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Array2DRowEnumerable(T[,] array, int row) { + if ((uint)row >= (uint)array.GetLength(0)) + { + ThrowArgumentOutOfRangeExceptionForInvalidRow(); + } + this.array = array; this.row = row; + this.width = array.GetLength(1); + this.column = -1; } /// /// Implements the duck-typed method. /// - /// An instance targeting the current 2D array instance. + /// An instance targeting the current 2D array instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Array2DRowEnumerable GetEnumerator() => this; + + /// + /// Implements the duck-typed method. + /// + /// whether a new element is available, otherwise [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(this.array, this.row); + public bool MoveNext() + { + int column = this.column + 1; + + if (column < this.width) + { + this.column = column; + + return true; + } + + return false; + } + + /// + /// Gets the duck-typed property. + /// + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + // This type is never used on .NET Core runtimes, where + // the fast indexer is available. Therefore, we can just + // use the built-in indexer for 2D arrays to access the value. + return ref this.array[this.row, this.column]; + } + } /// /// Returns a array with the values in the target row. @@ -77,86 +128,6 @@ public T[] ToArray() return array; } - /// - /// An enumerator for a source 2D array instance. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Enumerator - { - /// - /// The source 2D array instance. - /// - private readonly T[,] array; - - /// - /// The target row to iterate within . - /// - private readonly int row; - - /// - /// The width of a row in . - /// - private readonly int width; - - /// - /// The current column. - /// - private int column; - - /// - /// Initializes a new instance of the struct. - /// - /// The source 2D array instance. - /// The target row to iterate within . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator(T[,] array, int row) - { - if ((uint)row >= (uint)array.GetLength(0)) - { - ThrowArgumentOutOfRangeExceptionForInvalidRow(); - } - - this.array = array; - this.row = row; - this.width = array.GetLength(1); - this.column = -1; - } - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int column = this.column + 1; - - if (column < this.width) - { - this.column = column; - - return true; - } - - return false; - } - - /// - /// Gets the duck-typed property. - /// - public ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - // This type is never used on .NET Core runtimes, where - // the fast indexer is available. Therefore, we can just - // use the built-in indexer for 2D arrays to access the value. - return ref this.array[this.row, this.column]; - } - } - } - /// /// Throws an when the is invalid. /// From a4cd9494db147be0aca5f4ca88e0fb570760dfc0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 11 Jun 2020 02:15:43 +0200 Subject: [PATCH 3/3] Reverted changes for 2D array enumerables - The one for rows is not needed from .NET Core 2.1 and above - The other has the extra ToArray API which needs the initial values to remain available, and the type to be readonly --- .../Enumerables/Array2DColumnEnumerable{T}.cs | 227 ++++++++++-------- .../Enumerables/Array2DRowEnumerable{T}.cs | 141 ++++++----- 2 files changed, 213 insertions(+), 155 deletions(-) diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs index 37d53196e8d..6cd0ceaf9f3 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DColumnEnumerable{T}.cs @@ -16,35 +16,10 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Array2DColumnEnumerable + public readonly ref struct Array2DColumnEnumerable { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance mapping the target 2D array. - /// - /// - /// In runtimes where we have support for the type, we can - /// create one from the input 2D array and use that to traverse the target column. - /// This reduces the number of operations to perform for the offsetting to the right - /// column element (we simply need to add to the offset at each - /// iteration to move down by one row), and allows us to use the fast - /// accessor instead of the slower indexer for 2D arrays, as we can then access each - /// individual item linearly, since we know the absolute offset from the base location. - /// - private readonly Span span; - - /// - /// The width of the target 2D array. - /// - private readonly int width; - - /// - /// The current absolute offset within . - /// - private int offset; -#else /// - /// The source 2D array instance. + /// The source 2D array instance. /// private readonly T[,] array; @@ -53,93 +28,24 @@ public ref struct Array2DColumnEnumerable /// private readonly int column; - /// - /// The height of a column in . - /// - private readonly int height; - - /// - /// The current row. - /// - private int row; -#endif - /// /// Initializes a new instance of the struct. /// - /// The source 2D array instance. + /// The source 2D array instance. /// The target column to iterate within . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Array2DColumnEnumerable(T[,] array, int column) { - if ((uint)column >= (uint)array.GetLength(1)) - { - ThrowArgumentOutOfRangeExceptionForInvalidColumn(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = array.AsSpan(); - this.width = array.GetLength(1); - this.offset = column - this.width; -#else this.array = array; this.column = column; - this.height = array.GetLength(0); - this.row = -1; -#endif } /// /// Implements the duck-typed method. /// - /// An instance targeting the current 2D array instance. + /// An instance targeting the current 2D array instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Array2DColumnEnumerable GetEnumerator() => this; - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { -#if SPAN_RUNTIME_SUPPORT - int offset = this.offset + this.width; - - if ((uint)offset < (uint)this.span.Length) - { - this.offset = offset; - - return true; - } -#else - int row = this.row + 1; - - if (row < this.height) - { - this.row = row; - - return true; - } -#endif - return false; - } - - /// - /// Gets the duck-typed property. - /// - public ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return ref this.span.DangerousGetReferenceAt(this.offset); -#else - return ref this.array[this.row, this.column]; -#endif - } - } + public Enumerator GetEnumerator() => new Enumerator(this.array, this.column); /// /// Returns a array with the values in the target column. @@ -173,6 +79,129 @@ public T[] ToArray() return array; } + /// + /// An enumerator for a source 2D array instance. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ref struct Enumerator + { +#if SPAN_RUNTIME_SUPPORT + /// + /// The instance mapping the target 2D array. + /// + /// + /// In runtimes where we have support for the type, we can + /// create one from the input 2D array and use that to traverse the target column. + /// This reduces the number of operations to perform for the offsetting to the right + /// column element (we simply need to add to the offset at each + /// iteration to move down by one row), and allows us to use the fast + /// accessor instead of the slower indexer for 2D arrays, as we can then access each + /// individual item linearly, since we know the absolute offset from the base location. + /// + private readonly Span span; + + /// + /// The width of the target 2D array. + /// + private readonly int width; + + /// + /// The current absolute offset within . + /// + private int offset; +#else + /// + /// The source 2D array instance. + /// + private readonly T[,] array; + + /// + /// The target column to iterate within . + /// + private readonly int column; + + /// + /// The height of a column in . + /// + private readonly int height; + + /// + /// The current row. + /// + private int row; +#endif + + /// + /// Initializes a new instance of the struct. + /// + /// The source 2D array instance. + /// The target column to iterate within . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(T[,] array, int column) + { + if ((uint)column >= (uint)array.GetLength(1)) + { + ThrowArgumentOutOfRangeExceptionForInvalidColumn(); + } + +#if SPAN_RUNTIME_SUPPORT + this.span = array.AsSpan(); + this.width = array.GetLength(1); + this.offset = column - this.width; +#else + this.array = array; + this.column = column; + this.height = array.GetLength(0); + this.row = -1; +#endif + } + + /// + /// Implements the duck-typed method. + /// + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { +#if SPAN_RUNTIME_SUPPORT + int offset = this.offset + this.width; + + if ((uint)offset < (uint)this.span.Length) + { + this.offset = offset; + + return true; + } +#else + int row = this.row + 1; + + if (row < this.height) + { + this.row = row; + + return true; + } +#endif + return false; + } + + /// + /// Gets the duck-typed property. + /// + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if SPAN_RUNTIME_SUPPORT + return ref this.span.DangerousGetReferenceAt(this.offset); +#else + return ref this.array[this.row, this.column]; +#endif + } + } + } + /// /// Throws an when the is invalid. /// diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs index 7f21bba5f2d..a0d1fe98f5c 100644 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs +++ b/Microsoft.Toolkit.HighPerformance/Enumerables/Array2DRowEnumerable{T}.cs @@ -18,10 +18,10 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables /// /// The type of items to enumerate. [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct Array2DRowEnumerable + public readonly ref struct Array2DRowEnumerable { /// - /// The source 2D array instance. + /// The source 2D array instance. /// private readonly T[,] array; @@ -30,75 +30,24 @@ public ref struct Array2DRowEnumerable /// private readonly int row; - /// - /// The width of a row in . - /// - private readonly int width; - - /// - /// The current column. - /// - private int column; - /// /// Initializes a new instance of the struct. /// - /// The source 2D array instance. + /// The source 2D array instance. /// The target row to iterate within . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Array2DRowEnumerable(T[,] array, int row) { - if ((uint)row >= (uint)array.GetLength(0)) - { - ThrowArgumentOutOfRangeExceptionForInvalidRow(); - } - this.array = array; this.row = row; - this.width = array.GetLength(1); - this.column = -1; } /// /// Implements the duck-typed method. /// - /// An instance targeting the current 2D array instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Array2DRowEnumerable GetEnumerator() => this; - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise + /// An instance targeting the current 2D array instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int column = this.column + 1; - - if (column < this.width) - { - this.column = column; - - return true; - } - - return false; - } - - /// - /// Gets the duck-typed property. - /// - public ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - // This type is never used on .NET Core runtimes, where - // the fast indexer is available. Therefore, we can just - // use the built-in indexer for 2D arrays to access the value. - return ref this.array[this.row, this.column]; - } - } + public Enumerator GetEnumerator() => new Enumerator(this.array, this.row); /// /// Returns a array with the values in the target row. @@ -128,6 +77,86 @@ public T[] ToArray() return array; } + /// + /// An enumerator for a source 2D array instance. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ref struct Enumerator + { + /// + /// The source 2D array instance. + /// + private readonly T[,] array; + + /// + /// The target row to iterate within . + /// + private readonly int row; + + /// + /// The width of a row in . + /// + private readonly int width; + + /// + /// The current column. + /// + private int column; + + /// + /// Initializes a new instance of the struct. + /// + /// The source 2D array instance. + /// The target row to iterate within . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(T[,] array, int row) + { + if ((uint)row >= (uint)array.GetLength(0)) + { + ThrowArgumentOutOfRangeExceptionForInvalidRow(); + } + + this.array = array; + this.row = row; + this.width = array.GetLength(1); + this.column = -1; + } + + /// + /// Implements the duck-typed method. + /// + /// whether a new element is available, otherwise + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + int column = this.column + 1; + + if (column < this.width) + { + this.column = column; + + return true; + } + + return false; + } + + /// + /// Gets the duck-typed property. + /// + public ref T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + // This type is never used on .NET Core runtimes, where + // the fast indexer is available. Therefore, we can just + // use the built-in indexer for 2D arrays to access the value. + return ref this.array[this.row, this.column]; + } + } + } + /// /// Throws an when the is invalid. ///