From aa617ec7dfa5bd570d2d12bad9a8c53f9c2047f1 Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Mon, 10 Jun 2024 12:54:58 -0600 Subject: [PATCH 1/7] TensorShape --- .../src/System.Numerics.Tensors.csproj | 1 + .../Tensors/netcore/ReadOnlyTensorSpan.cs | 105 ++++++---------- .../Numerics/Tensors/netcore/TensorShape.cs | 59 +++++++++ .../Numerics/Tensors/netcore/TensorSpan.cs | 118 +++++++----------- 4 files changed, 141 insertions(+), 142 deletions(-) create mode 100644 src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs diff --git a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj index 2b656cc1d3ee5c..9f99e54449ec93 100644 --- a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj +++ b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj @@ -16,6 +16,7 @@ + diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs index a823f92e37b8e6..f69a6395a2f501 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs @@ -26,14 +26,8 @@ public readonly ref struct ReadOnlyTensorSpan { /// A byref or a native ptr. internal readonly ref T _reference; - /// The number of elements this ReadOnlyTensorSpan contains. - internal readonly nint _flattenedLength; - /// The length of the underlying memory. Can be different than the number of elements in the span. - internal readonly nint _memoryLength; - /// The lengths of each dimension. - internal readonly ReadOnlySpan _lengths; - /// The strides representing the memory offsets for each dimension. - private readonly ReadOnlySpan _strides; + internal readonly TensorShape _shape; + /// /// Creates a new span over the entirety of the target array. @@ -109,12 +103,8 @@ public ReadOnlyTensorSpan(T[]? array, int start, scoped ReadOnlySpan lengt ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); } - _flattenedLength = linearLength; - _memoryLength = array.Length; + _shape = new TensorShape(array.Length - start, lengths, strides); _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -144,12 +134,8 @@ public ReadOnlyTensorSpan(ReadOnlySpan span, scoped ReadOnlySpan length if (maxElements >= span.Length && span.Length != 0) ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - _flattenedLength = linearLength; - _memoryLength = span.Length; + _shape = new TensorShape(span.Length, lengths, strides); _reference = ref MemoryMarshal.GetReference(span); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -200,12 +186,8 @@ public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan start, scoped R ThrowHelper.ThrowArgumentOutOfRangeException(); } - _flattenedLength = linearLength; - _memoryLength = array.Length; + _shape = new TensorShape(array.Length - startOffset, lengths, strides); _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -249,12 +231,8 @@ public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan startIndex, ThrowHelper.ThrowArgumentOutOfRangeException(); } - _flattenedLength = linearLength; - _memoryLength = array.Length; + _shape = new TensorShape(array.Length - startOffset, lengths, strides); _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -303,13 +281,8 @@ public unsafe ReadOnlyTensorSpan(T* data, nint dataLength, scoped ReadOnlySpan= dataLength && dataLength != 0) ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - _flattenedLength = linearLength; - _memoryLength = dataLength; - + _shape = new TensorShape(dataLength, lengths, strides); _reference = ref *data; - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } // Constructor for internal use only. It is not safe to expose publicly. @@ -318,12 +291,8 @@ internal ReadOnlyTensorSpan(ref T reference, scoped ReadOnlySpan lengths, { nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - _flattenedLength = linearLength; - _memoryLength = memoryLength; + _shape = new TensorShape(memoryLength, lengths, strides); _reference = ref reference; - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -343,7 +312,7 @@ public ref readonly T this[params scoped ReadOnlySpan indexes] ThrowHelper.ThrowIndexOutOfRangeException(); nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _memoryLength || index < 0) + if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); return ref Unsafe.Add(ref _reference, index /* force zero-extension */); @@ -366,7 +335,7 @@ public ref readonly T this[params scoped ReadOnlySpan indexes] ThrowHelper.ThrowIndexOutOfRangeException(); nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _memoryLength || index < 0) + if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); return ref Unsafe.Add(ref _reference, index /* force zero-extension */); @@ -395,19 +364,19 @@ public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] /// /// The number of items in the span. /// - public nint FlattenedLength => _flattenedLength; + public nint FlattenedLength => _shape.FlattenedLength; /// /// Gets a value indicating whether this is empty. /// /// if this span is empty; otherwise, . - public bool IsEmpty => _flattenedLength == 0; + public bool IsEmpty => _shape.FlattenedLength == 0; /// /// Gets the length of each dimension in this . /// [UnscopedRef] - public ReadOnlySpan Lengths => _lengths; + public ReadOnlySpan Lengths => _shape.Lengths; /// /// Gets the rank, aka the number of dimensions, of this . @@ -418,7 +387,7 @@ public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] /// Gets the strides of this /// [UnscopedRef] - public ReadOnlySpan Strides => _strides; + public ReadOnlySpan Strides => _shape.Strides; /// /// Returns false if left and right point at the same memory and have the same length. Note that @@ -431,9 +400,9 @@ public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] /// this does *not* check to see if the *contents* are equal. /// public static bool operator ==(ReadOnlyTensorSpan left, ReadOnlyTensorSpan right) => - left._flattenedLength == right._flattenedLength && + left._shape.FlattenedLength == right._shape.FlattenedLength && left.Rank == right.Rank && - left._lengths.SequenceEqual(right._lengths )&& + left._shape.Lengths.SequenceEqual(right._shape.Lengths )&& Unsafe.AreSame(ref left._reference, ref right._reference); /// @@ -474,7 +443,7 @@ public override int GetHashCode() => /// This method uses a covariant cast, producing a read-only span that shares the same memory as the source. The relationships expressed in the type constraints ensure that the cast is a safe operation. public static ReadOnlyTensorSpan CastUp(ReadOnlyTensorSpan items) where TDerived : class?, T { - return new ReadOnlyTensorSpan(ref Unsafe.As(ref items._reference), items._lengths, items._strides, items._memoryLength); + return new ReadOnlyTensorSpan(ref Unsafe.As(ref items._reference), items._shape.Lengths, items._shape.Strides, items._shape._memoryLength); } /// Gets an enumerator for this span. @@ -530,7 +499,7 @@ public ref readonly T GetPinnableReference() { // Ensure that the native code has just one forward branch that is predicted-not-taken. ref T ret = ref Unsafe.NullRef(); - if (_flattenedLength != 0) ret = ref _reference; + if (_shape.FlattenedLength != 0) ret = ref _reference; return ref ret; } @@ -549,7 +518,7 @@ public void CopyTo(scoped TensorSpan destination) // Using "if (!TryCopyTo(...))" results in two branches: one for the length // check, and one for the result of TryCopyTo. Since these checks are equivalent, // we can optimize by performing the check once ourselves then calling Memmove directly. - if (_flattenedLength <= destination.FlattenedLength) + if (_shape.FlattenedLength <= destination.FlattenedLength) { scoped Span curIndexes; nint[]? curIndexesArray; @@ -565,14 +534,14 @@ public void CopyTo(scoped TensorSpan destination) } nint copiedValues = 0; - TensorSpan slice = destination.Slice(_lengths); - while (copiedValues < _flattenedLength) + TensorSpan slice = destination.Slice(_shape.Lengths); + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(ref Unsafe.Add(ref slice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } - Debug.Assert(copiedValues == _flattenedLength, "Didn't copy the right amount to the array."); + Debug.Assert(copiedValues == _shape.FlattenedLength, "Didn't copy the right amount to the array."); if (curIndexesArray != null) ArrayPool.Shared.Return(curIndexesArray); @@ -595,7 +564,7 @@ public bool TryCopyTo(scoped TensorSpan destination) { bool retVal = false; - if (_flattenedLength <= destination.FlattenedLength) + if (_shape.FlattenedLength <= destination.FlattenedLength) { scoped Span curIndexes; nint[]? curIndexesArray; @@ -611,15 +580,15 @@ public bool TryCopyTo(scoped TensorSpan destination) } nint copiedValues = 0; - TensorSpan slice = destination.Slice(_lengths); - while (copiedValues < _flattenedLength) + TensorSpan slice = destination.Slice(_shape.Lengths); + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(ref Unsafe.Add(ref slice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } retVal = true; - Debug.Assert(copiedValues == _flattenedLength, "Didn't copy the right amount to the array."); + Debug.Assert(copiedValues == _shape.FlattenedLength, "Didn't copy the right amount to the array."); if (curIndexesArray != null) ArrayPool.Shared.Return(curIndexesArray); @@ -634,7 +603,7 @@ public bool TryCopyTo(scoped TensorSpan destination) /// /// Returns a with the name of the type and the number of elements. /// - public override string ToString() => $"System.Numerics.Tensors.ReadOnlyTensorSpan<{typeof(T).Name}>[{_flattenedLength}]"; + public override string ToString() => $"System.Numerics.Tensors.ReadOnlyTensorSpan<{typeof(T).Name}>[{_shape.FlattenedLength}]"; /// /// Returns a reference to specified element of the TensorSpan. @@ -704,10 +673,10 @@ public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) index += Strides[i] * (offsets[i]); } - if (index >= _memoryLength || index < 0) + if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); - return new ReadOnlyTensorSpan(ref Unsafe.Add(ref _reference, index), shape, _strides, _memoryLength - index); + return new ReadOnlyTensorSpan(ref Unsafe.Add(ref _reference, index), shape, _shape.Strides, _shape._memoryLength - index); } /// @@ -717,7 +686,7 @@ public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) public bool TryFlattenTo(scoped Span destination) { bool retVal = false; - if (destination.Length < _flattenedLength) + if (destination.Length < _shape.FlattenedLength) { scoped Span curIndexes; nint[]? curIndexesArray; @@ -733,10 +702,10 @@ public bool TryFlattenTo(scoped Span destination) } nint copiedValues = 0; - while (copiedValues < _flattenedLength) + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } @@ -753,10 +722,10 @@ public bool TryFlattenTo(scoped Span destination) /// The span to copy items into. public void FlattenTo(scoped Span destination) { - if (destination.Length < _flattenedLength) + if (destination.Length < _shape.FlattenedLength) ThrowHelper.ThrowArgumentException_DestinationTooShort(); - if (_flattenedLength == 0) + if (_shape.FlattenedLength == 0) return; scoped Span curIndexes; @@ -773,7 +742,7 @@ public void FlattenTo(scoped Span destination) } nint copiedValues = 0; - while (copiedValues < _flattenedLength) + while (copiedValues < _shape.FlattenedLength) { if (Strides[Rank - 1] == 0) { diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs new file mode 100644 index 00000000000000..2202804577644a --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Data; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net.NetworkInformation; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace System.Numerics.Tensors +{ + internal struct TensorShape + { + internal nint[]? _metadata; // 8 bytes + +#pragma warning disable CA1823 // Avoid unused private fields + internal nint _memoryLength; // 8 bytes +#pragma warning restore CA1823 // Avoid unused private fields + internal int _rank; // 4 bytes + + private LengthsBuffer _lengths; + private StridesBuffer _strides; + + internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan strides) + { + _memoryLength = memoryLength; + _rank = Lengths.Length; + lengths.CopyTo(_lengths); + strides.CopyTo(_strides); + } + + [InlineArray(5)] // 5x8 bytes (40) + private struct LengthsBuffer + { + public nint e0; + } + + [InlineArray(5)] // 5x8 bytes (40) + private struct StridesBuffer + { + public nint e0; + } + + [UnscopedRef] + public ReadOnlySpan Lengths => (_metadata is null) ? ((ReadOnlySpan)_lengths).Slice(0, _rank) : MemoryMarshal.CreateReadOnlySpan(in _metadata[0], _rank); + + [UnscopedRef] + public ReadOnlySpan Strides => (_metadata is null) ? ((ReadOnlySpan)_strides).Slice(0, _rank) : MemoryMarshal.CreateReadOnlySpan(in _metadata[_metadata.Length / 2], _rank); + + public nint FlattenedLength => TensorSpanHelpers.CalculateTotalLength(Lengths); + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index b6e8826cb25569..57023f561f80b0 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -26,14 +26,7 @@ public readonly ref struct TensorSpan { /// A byref or a native ptr. internal readonly ref T _reference; - /// The number of elements this TensorSpan contains. - internal readonly nint _flattenedLength; - /// The length of the underlying memory. Can be different than the number of elements in the span. - internal readonly nint _memoryLength; - /// The lengths of each dimension. - internal readonly ReadOnlySpan _lengths; - /// The strides representing the memory offsets for each dimension. - private readonly ReadOnlySpan _strides; + internal readonly TensorShape _shape; /// @@ -111,12 +104,8 @@ public TensorSpan(T[]? array, int start, scoped ReadOnlySpan lengths, scop ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); } - _flattenedLength = linearLength; - _memoryLength = array.Length - start; + _shape = new TensorShape(array.Length - start, lengths, strides); _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -147,12 +136,8 @@ public TensorSpan(Span span, scoped ReadOnlySpan lengths, scoped ReadOn if (maxElements >= span.Length && span.Length != 0) ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - _flattenedLength = linearLength; - _memoryLength = span.Length; + _shape = new TensorShape(span.Length, lengths, strides); _reference = ref MemoryMarshal.GetReference(span); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -204,12 +189,8 @@ public TensorSpan(Array? array, scoped ReadOnlySpan start, scoped ReadOnlyS ThrowHelper.ThrowArgumentOutOfRangeException(); } - _flattenedLength = linearLength; - _memoryLength = array.Length; + _shape = new TensorShape(array.Length - startOffset, lengths, strides); _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -252,12 +233,8 @@ public TensorSpan(Array? array, scoped ReadOnlySpan startIndex, scoped R ThrowHelper.ThrowArgumentOutOfRangeException(); } - _flattenedLength = linearLength; - _memoryLength = array.Length; + _shape = new TensorShape(array.Length, lengths, strides); _reference = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), (nint)(uint)startOffset /* force zero-extension */); - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -306,12 +283,9 @@ public unsafe TensorSpan(T* data, nint dataLength, scoped ReadOnlySpan len nint maxElements = TensorSpanHelpers.ComputeMaxLinearIndex(strides, lengths); if (maxElements >= dataLength && dataLength != 0) ThrowHelper.ThrowArgument_InvalidStridesAndLengths(); - _flattenedLength = linearLength; - _memoryLength = dataLength; - _reference = ref *data; - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); + _shape = new TensorShape(dataLength, lengths, strides); + _reference = ref *data; } // Constructor for internal use only. It is not safe to expose publicly. @@ -320,12 +294,8 @@ internal TensorSpan(ref T reference, scoped ReadOnlySpan lengths, scoped R { nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths); - _flattenedLength = linearLength; - _memoryLength = memoryLength; + _shape = new TensorShape(memoryLength, lengths, strides); _reference = ref reference; - - _lengths = lengths.ToArray(); - _strides = strides.ToArray(); } /// @@ -345,7 +315,7 @@ public ref T this[params scoped ReadOnlySpan indexes] ThrowHelper.ThrowIndexOutOfRangeException(); nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _memoryLength || index < 0) + if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); return ref Unsafe.Add(ref _reference, index /* force zero-extension */); @@ -370,7 +340,7 @@ public ref T this[params scoped ReadOnlySpan indexes] ThrowHelper.ThrowIndexOutOfRangeException(); nint index = TensorSpanHelpers.ComputeLinearIndex(indexes, Strides, Lengths); - if (index >= _memoryLength || index < 0) + if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); return ref Unsafe.Add(ref _reference, index /* force zero-extension */); @@ -401,19 +371,19 @@ public TensorSpan this[params scoped ReadOnlySpan ranges] /// /// The number of items in the span. /// - public nint FlattenedLength => _flattenedLength; + public nint FlattenedLength => _shape.FlattenedLength; /// /// Gets a value indicating whether this is empty. /// /// if this span is empty; otherwise, . - public bool IsEmpty => _flattenedLength == 0; + public bool IsEmpty => _shape.FlattenedLength == 0; /// /// Gets the length of each dimension in this . /// [UnscopedRef] - public ReadOnlySpan Lengths => _lengths; + public ReadOnlySpan Lengths => _shape.Lengths; /// /// Gets the rank, aka the number of dimensions, of this . @@ -424,7 +394,7 @@ public TensorSpan this[params scoped ReadOnlySpan ranges] /// Gets the strides of this /// [UnscopedRef] - public ReadOnlySpan Strides => _strides; + public ReadOnlySpan Strides => _shape.Strides; /// /// Returns false if left and right point at the same memory and have the same length. Note that @@ -437,9 +407,9 @@ public TensorSpan this[params scoped ReadOnlySpan ranges] /// this does *not* check to see if the *contents* are equal. /// public static bool operator ==(TensorSpan left, TensorSpan right) => - left._flattenedLength == right._flattenedLength && + left._shape.FlattenedLength == right._shape.FlattenedLength && left.Rank == right.Rank && - left._lengths.SequenceEqual(right._lengths) && + left._shape.Lengths.SequenceEqual(right._shape.Lengths) && Unsafe.AreSame(ref left._reference, ref right._reference); /// @@ -523,7 +493,7 @@ public ref T GetPinnableReference() { // Ensure that the native code has just one forward branch that is predicted-not-taken. ref T ret = ref Unsafe.NullRef(); - if (_flattenedLength != 0) ret = ref _reference; + if (_shape.FlattenedLength != 0) ret = ref _reference; return ref ret; } @@ -547,13 +517,13 @@ public void Clear() } nint clearedValues = 0; - while (clearedValues < _flattenedLength) + while (clearedValues < _shape.FlattenedLength) { TensorSpanHelpers.Clear(ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), (nuint)Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); clearedValues += Lengths[Rank - 1]; } - Debug.Assert(clearedValues == _flattenedLength, "Didn't clear the right amount"); + Debug.Assert(clearedValues == _shape.FlattenedLength, "Didn't clear the right amount"); if (curIndexesArray != null) ArrayPool.Shared.Return(curIndexesArray); @@ -568,14 +538,14 @@ public void Fill(T value) Span curIndexes = stackalloc nint[Rank]; nint filledValues = 0; // REVIEW: If we track the actual length of the backing data, because FlattenedLength doesn't always equal the actual length, we could use that here to not need to loop. - while (filledValues < _flattenedLength) + while (filledValues < _shape.FlattenedLength) { TensorSpanHelpers.Fill(ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), (nuint)Lengths[Rank - 1], value); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); filledValues += Lengths[Rank - 1]; } - Debug.Assert(filledValues == _flattenedLength, "Didn't copy the right amount to the array."); + Debug.Assert(filledValues == _shape.FlattenedLength, "Didn't copy the right amount to the array."); } @@ -594,7 +564,7 @@ public void CopyTo(scoped TensorSpan destination) // Using "if (!TryCopyTo(...))" results in two branches: one for the length // check, and one for the result of TryCopyTo. Since these checks are equivalent, // we can optimize by performing the check once ourselves then calling Memmove directly. - if (_flattenedLength > destination.FlattenedLength) + if (_shape.FlattenedLength > destination.FlattenedLength) ThrowHelper.ThrowArgumentException_DestinationTooShort(); scoped Span curIndexes; @@ -611,14 +581,14 @@ public void CopyTo(scoped TensorSpan destination) } nint copiedValues = 0; - TensorSpan slice = destination.Slice(_lengths); - while (copiedValues < _flattenedLength) + TensorSpan slice = destination.Slice(_shape.Lengths); + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(ref Unsafe.Add(ref slice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, destination.Strides, Lengths)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } - Debug.Assert(copiedValues == _flattenedLength, "Didn't copy the right amount to the array."); + Debug.Assert(copiedValues == _shape.FlattenedLength, "Didn't copy the right amount to the array."); if (curIndexesArray != null) ArrayPool.Shared.Return(curIndexesArray); @@ -637,7 +607,7 @@ public bool TryCopyTo(scoped TensorSpan destination) { bool retVal = false; - if (_flattenedLength <= destination.FlattenedLength) + if (_shape.FlattenedLength <= destination.FlattenedLength) { scoped Span curIndexes; nint[]? curIndexesArray; @@ -653,15 +623,15 @@ public bool TryCopyTo(scoped TensorSpan destination) } nint copiedValues = 0; - TensorSpan slice = destination.Slice(_lengths); - while (copiedValues < _flattenedLength) + TensorSpan slice = destination.Slice(_shape.Lengths); + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(ref Unsafe.Add(ref slice._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } retVal = true; - Debug.Assert(copiedValues == _flattenedLength, "Didn't copy the right amount to the array."); + Debug.Assert(copiedValues == _shape.FlattenedLength, "Didn't copy the right amount to the array."); if (curIndexesArray != null) ArrayPool.Shared.Return(curIndexesArray); @@ -676,13 +646,13 @@ public bool TryCopyTo(scoped TensorSpan destination) /// Defines an implicit conversion of a to a /// public static implicit operator ReadOnlyTensorSpan(TensorSpan span) => - new ReadOnlyTensorSpan(ref span._reference, span._lengths, span._strides, span._memoryLength); + new ReadOnlyTensorSpan(ref span._reference, span._shape.Lengths, span._shape.Strides, span._shape._memoryLength); /// /// For , returns a new instance of string that represents the characters pointed to by the span. /// Otherwise, returns a with the name of the type and the number of elements. /// - public override string ToString() => $"System.Numerics.Tensors.TensorSpan<{typeof(T).Name}>[{_flattenedLength}]"; + public override string ToString() => $"System.Numerics.Tensors.TensorSpan<{typeof(T).Name}>[{_shape.FlattenedLength}]"; /// /// Returns a reference to specified element of the TensorSpan. @@ -745,10 +715,10 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) index += Strides[i] * (offsets[i]); } - if (index >= _memoryLength || index < 0) + if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); - return new TensorSpan(ref Unsafe.Add(ref _reference, index), lengths, _strides, _memoryLength - index); + return new TensorSpan(ref Unsafe.Add(ref _reference, index), _shape.Lengths, _shape.Strides, _shape._memoryLength - index); } /// @@ -758,7 +728,7 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) public bool TryFlattenTo(scoped Span destination) { bool retVal = false; - if (destination.Length < _flattenedLength) + if (destination.Length < _shape.FlattenedLength) { scoped Span curIndexes; nint[]? curIndexesArray; @@ -774,10 +744,10 @@ public bool TryFlattenTo(scoped Span destination) } nint copiedValues = 0; - while (copiedValues < _flattenedLength) + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } @@ -794,10 +764,10 @@ public bool TryFlattenTo(scoped Span destination) /// The span to copy items into. public void FlattenTo(scoped Span destination) { - if (destination.Length < _flattenedLength) + if (destination.Length < _shape.FlattenedLength) ThrowHelper.ThrowArgumentException_DestinationTooShort(); - if (_flattenedLength == 0) + if (_shape.FlattenedLength == 0) return; scoped Span curIndexes; @@ -814,10 +784,10 @@ public void FlattenTo(scoped Span destination) } nint copiedValues = 0; - while (copiedValues < _flattenedLength) + while (copiedValues < _shape.FlattenedLength) { TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } From c4acef88bbac884c23ed54965960e0fe103f8abf Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Mon, 10 Jun 2024 13:51:02 -0600 Subject: [PATCH 2/7] fixed TensorShape issue --- .../src/System.Numerics.Tensors.csproj | 2 +- .../Numerics/Tensors/netcore/TensorShape.cs | 37 +++++++++++++------ .../Tensors/netcore/TensorSpanHelpers.cs | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj index 9f99e54449ec93..01b01181a0a4e6 100644 --- a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj +++ b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 2202804577644a..3855c397853979 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -16,24 +16,31 @@ namespace System.Numerics.Tensors { - internal struct TensorShape + public readonly struct TensorShape { - internal nint[]? _metadata; // 8 bytes + internal readonly nint[]? _metadata; // 8 bytes -#pragma warning disable CA1823 // Avoid unused private fields - internal nint _memoryLength; // 8 bytes -#pragma warning restore CA1823 // Avoid unused private fields - internal int _rank; // 4 bytes + internal readonly nint _memoryLength; // 8 bytes + internal readonly int _rank; // 4 bytes - private LengthsBuffer _lengths; - private StridesBuffer _strides; + private readonly LengthsBuffer _lengths; + private readonly StridesBuffer _strides; internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan strides) { _memoryLength = memoryLength; _rank = Lengths.Length; - lengths.CopyTo(_lengths); - strides.CopyTo(_strides); + if (lengths.Length > 5) + { + _metadata = new nint[lengths.Length + strides.Length]; + lengths.CopyTo(MemoryMarshal.CreateSpan(ref _metadata[0], lengths.Length)); + strides.CopyTo(MemoryMarshal.CreateSpan(ref _metadata[lengths.Length], strides.Length)); + } + else + { + lengths.CopyTo(_lengths); + strides.CopyTo(_strides); + } } [InlineArray(5)] // 5x8 bytes (40) @@ -49,10 +56,16 @@ private struct StridesBuffer } [UnscopedRef] - public ReadOnlySpan Lengths => (_metadata is null) ? ((ReadOnlySpan)_lengths).Slice(0, _rank) : MemoryMarshal.CreateReadOnlySpan(in _metadata[0], _rank); +#pragma warning disable CS9192 // Argument should be passed with 'ref' or 'in' keyword + public ReadOnlySpan Lengths => (_metadata is null) + ? ((ReadOnlySpan)_lengths).Slice(0, _rank) + : MemoryMarshal.CreateReadOnlySpan(MemoryMarshal.GetArrayDataReference(_metadata), _rank); [UnscopedRef] - public ReadOnlySpan Strides => (_metadata is null) ? ((ReadOnlySpan)_strides).Slice(0, _rank) : MemoryMarshal.CreateReadOnlySpan(in _metadata[_metadata.Length / 2], _rank); + public ReadOnlySpan Strides => (_metadata is null) + ? ((ReadOnlySpan)_strides).Slice(0, _rank) + : MemoryMarshal.CreateReadOnlySpan(MemoryMarshal.GetArrayDataReference(_metadata), _rank).Slice(_rank / 2); +#pragma warning restore CS9192 // Argument should be passed with 'ref' or 'in' keyword public nint FlattenedLength => TensorSpanHelpers.CalculateTotalLength(Lengths); } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs index 344226025c277a..041fd3d51df0c6 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs @@ -16,7 +16,7 @@ internal static partial class TensorSpanHelpers { internal static bool AreShapesTheSame(ReadOnlyTensorSpan tensor1, ReadOnlyTensorSpan tensor2) - where T : IEquatable, IEqualityOperators => tensor1._lengths.SequenceEqual(tensor2._lengths); + where T : IEquatable, IEqualityOperators => tensor1._shape.Lengths.SequenceEqual(tensor2._shape.Lengths); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static nint CalculateTotalLength(ReadOnlySpan lengths) From c02fd920badc71c44082d9e712dc268b56d091e0 Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Tue, 11 Jun 2024 14:59:08 -0600 Subject: [PATCH 3/7] removed extra inline buffer type. Fixed tests. Added large dimension testing --- .../Tensors/netcore/ReadOnlyTensorSpan.cs | 24 ++++--- .../Numerics/Tensors/netcore/TensorShape.cs | 32 +++------- .../Numerics/Tensors/netcore/TensorSpan.cs | 31 ++++++--- .../tests/ReadOnlyTensorSpanTests.cs | 64 +++++++++++++++++++ .../tests/TensorSpanTests.cs | 64 +++++++++++++++++++ .../tests/TensorTests.cs | 64 +++++++++++++++++++ 6 files changed, 237 insertions(+), 42 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs index f69a6395a2f501..625cf5d2c664bf 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs @@ -522,10 +522,11 @@ public void CopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -568,10 +569,11 @@ public bool TryCopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -649,22 +651,22 @@ public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) if (ranges.Length != Lengths.Length) throw new ArgumentOutOfRangeException(nameof(ranges), "Number of dimensions to slice does not equal the number of dimensions in the span"); - scoped Span shape; + scoped Span lengths; scoped Span offsets; - if (Rank > 6) + if (Rank > 5) { - shape = stackalloc nint[Rank]; + lengths = stackalloc nint[Rank]; offsets = stackalloc nint[Rank]; } else { - shape = new nint[Rank]; + lengths = new nint[Rank]; offsets = new nint[Rank]; } for (int i = 0; i < ranges.Length; i++) { - (offsets[i], shape[i]) = ranges[i].GetOffsetAndLength(Lengths[i]); + (offsets[i], lengths[i]) = ranges[i].GetOffsetAndLength(Lengths[i]); } nint index = 0; @@ -676,7 +678,7 @@ public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); - return new ReadOnlyTensorSpan(ref Unsafe.Add(ref _reference, index), shape, _shape.Strides, _shape._memoryLength - index); + return new ReadOnlyTensorSpan(ref Unsafe.Add(ref _reference, index), lengths, _shape.Strides, _shape._memoryLength - index); } /// @@ -690,10 +692,11 @@ public bool TryFlattenTo(scoped Span destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -730,10 +733,11 @@ public void FlattenTo(scoped Span destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 3855c397853979..549aee71bff107 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -1,35 +1,27 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Data; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net.NetworkInformation; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; namespace System.Numerics.Tensors { - public readonly struct TensorShape + internal readonly struct TensorShape { internal readonly nint[]? _metadata; // 8 bytes internal readonly nint _memoryLength; // 8 bytes internal readonly int _rank; // 4 bytes - private readonly LengthsBuffer _lengths; - private readonly StridesBuffer _strides; + private readonly NintBuffer _lengths; + private readonly NintBuffer _strides; internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan strides) { _memoryLength = memoryLength; - _rank = Lengths.Length; + _rank = lengths.Length; if (lengths.Length > 5) { _metadata = new nint[lengths.Length + strides.Length]; @@ -44,28 +36,20 @@ internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan } [InlineArray(5)] // 5x8 bytes (40) - private struct LengthsBuffer - { - public nint e0; - } - - [InlineArray(5)] // 5x8 bytes (40) - private struct StridesBuffer + private struct NintBuffer { public nint e0; } [UnscopedRef] -#pragma warning disable CS9192 // Argument should be passed with 'ref' or 'in' keyword public ReadOnlySpan Lengths => (_metadata is null) ? ((ReadOnlySpan)_lengths).Slice(0, _rank) - : MemoryMarshal.CreateReadOnlySpan(MemoryMarshal.GetArrayDataReference(_metadata), _rank); + : MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetArrayDataReference(_metadata), _rank); [UnscopedRef] public ReadOnlySpan Strides => (_metadata is null) ? ((ReadOnlySpan)_strides).Slice(0, _rank) - : MemoryMarshal.CreateReadOnlySpan(MemoryMarshal.GetArrayDataReference(_metadata), _rank).Slice(_rank / 2); -#pragma warning restore CS9192 // Argument should be passed with 'ref' or 'in' keyword + : MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetArrayDataReference(_metadata), _rank * 2).Slice(_rank); public nint FlattenedLength => TensorSpanHelpers.CalculateTotalLength(Lengths); } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index 57023f561f80b0..b85c11d29b427b 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -505,10 +505,11 @@ public void Clear() { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -569,10 +570,11 @@ public void CopyTo(scoped TensorSpan destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -611,10 +613,11 @@ public bool TryCopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -698,8 +701,18 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) if (ranges.Length != Lengths.Length) ThrowHelper.ThrowIndexOutOfRangeException(); - nint[] lengths = new nint[ranges.Length]; - nint[] offsets = new nint[ranges.Length]; + scoped Span lengths; + scoped Span offsets; + if (Rank > 5) + { + lengths = stackalloc nint[Rank]; + offsets = stackalloc nint[Rank]; + } + else + { + lengths = new nint[Rank]; + offsets = new nint[Rank]; + } for (int i = 0; i < ranges.Length; i++) { @@ -718,7 +731,7 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) if (index >= _shape._memoryLength || index < 0) ThrowHelper.ThrowIndexOutOfRangeException(); - return new TensorSpan(ref Unsafe.Add(ref _reference, index), _shape.Lengths, _shape.Strides, _shape._memoryLength - index); + return new TensorSpan(ref Unsafe.Add(ref _reference, index), lengths, _shape.Strides, _shape._memoryLength - index); } /// @@ -732,10 +745,11 @@ public bool TryFlattenTo(scoped Span destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { @@ -772,10 +786,11 @@ public void FlattenTo(scoped Span destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 6) + if (Rank > 5) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; + curIndexes = curIndexes.Slice(0, Rank); } else { diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index 70161522e4882b..e5e4a8c60bdc7b 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -752,6 +752,70 @@ public static unsafe void ReadOnlyTensorSpanPointerConstructorTests() } } + [Fact] + public static void ReadOnlyTensorSpanLargeDimensionsTests() + { + int[] a = { 91, 92, -93, 94, 95, -96 }; + int[] results = new int[6]; + ReadOnlyTensorSpan spanInt = a.AsTensorSpan(1, 1, 1, 1, 1, 6); + Assert.Equal(6, spanInt.Rank); + + Assert.Equal(6, spanInt.Lengths.Length); + Assert.Equal(1, spanInt.Lengths[0]); + Assert.Equal(1, spanInt.Lengths[1]); + Assert.Equal(1, spanInt.Lengths[2]); + Assert.Equal(1, spanInt.Lengths[3]); + Assert.Equal(1, spanInt.Lengths[4]); + Assert.Equal(6, spanInt.Lengths[5]); + Assert.Equal(6, spanInt.Strides.Length); + Assert.Equal(6, spanInt.Strides[0]); + Assert.Equal(6, spanInt.Strides[1]); + Assert.Equal(6, spanInt.Strides[2]); + Assert.Equal(6, spanInt.Strides[3]); + Assert.Equal(6, spanInt.Strides[4]); + Assert.Equal(1, spanInt.Strides[5]); + Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); + Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); + Assert.Equal(-93, spanInt[0, 0, 0, 0, 0, 2]); + Assert.Equal(94, spanInt[0, 0, 0, 0, 0, 3]); + Assert.Equal(95, spanInt[0, 0, 0, 0, 0, 4]); + Assert.Equal(-96, spanInt[0, 0, 0, 0, 0, 5]); + spanInt.FlattenTo(results); + Assert.Equal(a, results); + + a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; + results = new int[12]; + spanInt = a.AsTensorSpan(1, 2, 2, 1, 1, 3); + Assert.Equal(6, spanInt.Lengths.Length); + Assert.Equal(1, spanInt.Lengths[0]); + Assert.Equal(2, spanInt.Lengths[1]); + Assert.Equal(2, spanInt.Lengths[2]); + Assert.Equal(1, spanInt.Lengths[3]); + Assert.Equal(1, spanInt.Lengths[4]); + Assert.Equal(3, spanInt.Lengths[5]); + Assert.Equal(6, spanInt.Strides.Length); + Assert.Equal(12, spanInt.Strides[0]); + Assert.Equal(6, spanInt.Strides[1]); + Assert.Equal(3, spanInt.Strides[2]); + Assert.Equal(3, spanInt.Strides[3]); + Assert.Equal(3, spanInt.Strides[4]); + Assert.Equal(1, spanInt.Strides[5]); + Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); + Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); + Assert.Equal(-93, spanInt[0, 0, 0, 0, 0, 2]); + Assert.Equal(94, spanInt[0, 0, 1, 0, 0, 0]); + Assert.Equal(95, spanInt[0, 0, 1, 0, 0, 1]); + Assert.Equal(-96, spanInt[0, 0, 1, 0, 0, 2]); + Assert.Equal(-91, spanInt[0, 1, 0, 0, 0, 0]); + Assert.Equal(-92, spanInt[0, 1, 0, 0, 0, 1]); + Assert.Equal(93, spanInt[0, 1, 0, 0, 0, 2]); + Assert.Equal(-94, spanInt[0, 1, 1, 0, 0, 0]); + Assert.Equal(-95, spanInt[0, 1, 1, 0, 0, 1]); + Assert.Equal(96, spanInt[0, 1, 1, 0, 0, 2]); + spanInt.FlattenTo(results); + Assert.Equal(a, results); + } + [Fact] public static void IntArrayAsReadOnlyTensorSpan() { diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index fa79cae4883f87..7b9ba8f4272e06 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -987,6 +987,70 @@ public static unsafe void TensorSpanPointerConstructorTests() } } + [Fact] + public static void TensorSpanLargeDimensionsTests() + { + int[] a = { 91, 92, -93, 94, 95, -96 }; + int[] results = new int[6]; + ReadOnlyTensorSpan spanInt = a.AsTensorSpan(1, 1, 1, 1, 1, 6); + Assert.Equal(6, spanInt.Rank); + + Assert.Equal(6, spanInt.Lengths.Length); + Assert.Equal(1, spanInt.Lengths[0]); + Assert.Equal(1, spanInt.Lengths[1]); + Assert.Equal(1, spanInt.Lengths[2]); + Assert.Equal(1, spanInt.Lengths[3]); + Assert.Equal(1, spanInt.Lengths[4]); + Assert.Equal(6, spanInt.Lengths[5]); + Assert.Equal(6, spanInt.Strides.Length); + Assert.Equal(6, spanInt.Strides[0]); + Assert.Equal(6, spanInt.Strides[1]); + Assert.Equal(6, spanInt.Strides[2]); + Assert.Equal(6, spanInt.Strides[3]); + Assert.Equal(6, spanInt.Strides[4]); + Assert.Equal(1, spanInt.Strides[5]); + Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); + Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); + Assert.Equal(-93, spanInt[0, 0, 0, 0, 0, 2]); + Assert.Equal(94, spanInt[0, 0, 0, 0, 0, 3]); + Assert.Equal(95, spanInt[0, 0, 0, 0, 0, 4]); + Assert.Equal(-96, spanInt[0, 0, 0, 0, 0, 5]); + spanInt.FlattenTo(results); + Assert.Equal(a, results); + + a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; + results = new int[12]; + spanInt = a.AsTensorSpan(1, 2, 2, 1, 1, 3); + Assert.Equal(6, spanInt.Lengths.Length); + Assert.Equal(1, spanInt.Lengths[0]); + Assert.Equal(2, spanInt.Lengths[1]); + Assert.Equal(2, spanInt.Lengths[2]); + Assert.Equal(1, spanInt.Lengths[3]); + Assert.Equal(1, spanInt.Lengths[4]); + Assert.Equal(3, spanInt.Lengths[5]); + Assert.Equal(6, spanInt.Strides.Length); + Assert.Equal(12, spanInt.Strides[0]); + Assert.Equal(6, spanInt.Strides[1]); + Assert.Equal(3, spanInt.Strides[2]); + Assert.Equal(3, spanInt.Strides[3]); + Assert.Equal(3, spanInt.Strides[4]); + Assert.Equal(1, spanInt.Strides[5]); + Assert.Equal(91, spanInt[0, 0, 0, 0, 0, 0]); + Assert.Equal(92, spanInt[0, 0, 0, 0, 0, 1]); + Assert.Equal(-93, spanInt[0, 0, 0, 0, 0, 2]); + Assert.Equal(94, spanInt[0, 0, 1, 0, 0, 0]); + Assert.Equal(95, spanInt[0, 0, 1, 0, 0, 1]); + Assert.Equal(-96, spanInt[0, 0, 1, 0, 0, 2]); + Assert.Equal(-91, spanInt[0, 1, 0, 0, 0, 0]); + Assert.Equal(-92, spanInt[0, 1, 0, 0, 0, 1]); + Assert.Equal(93, spanInt[0, 1, 0, 0, 0, 2]); + Assert.Equal(-94, spanInt[0, 1, 1, 0, 0, 0]); + Assert.Equal(-95, spanInt[0, 1, 1, 0, 0, 1]); + Assert.Equal(96, spanInt[0, 1, 1, 0, 0, 2]); + spanInt.FlattenTo(results); + Assert.Equal(a, results); + } + [Fact] public static void IntArrayAsTensorSpan() { diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index da33195220ca8a..3477b10ae0ac25 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -230,6 +230,70 @@ public void TensorExtensionsTwoSpanInFloatOut(PerformCalculationTwoSpanInFloa #endregion + [Fact] + public static void TensorLargeDimensionsTests() + { + int[] a = { 91, 92, -93, 94, 95, -96 }; + int[] results = new int[6]; + Tensor tensor = Tensor.Create(a,[1, 1, 1, 1, 1, 6]); + Assert.Equal(6, tensor.Rank); + + Assert.Equal(6, tensor.Lengths.Length); + Assert.Equal(1, tensor.Lengths[0]); + Assert.Equal(1, tensor.Lengths[1]); + Assert.Equal(1, tensor.Lengths[2]); + Assert.Equal(1, tensor.Lengths[3]); + Assert.Equal(1, tensor.Lengths[4]); + Assert.Equal(6, tensor.Lengths[5]); + Assert.Equal(6, tensor.Strides.Length); + Assert.Equal(6, tensor.Strides[0]); + Assert.Equal(6, tensor.Strides[1]); + Assert.Equal(6, tensor.Strides[2]); + Assert.Equal(6, tensor.Strides[3]); + Assert.Equal(6, tensor.Strides[4]); + Assert.Equal(1, tensor.Strides[5]); + Assert.Equal(91, tensor[0, 0, 0, 0, 0, 0]); + Assert.Equal(92, tensor[0, 0, 0, 0, 0, 1]); + Assert.Equal(-93, tensor[0, 0, 0, 0, 0, 2]); + Assert.Equal(94, tensor[0, 0, 0, 0, 0, 3]); + Assert.Equal(95, tensor[0, 0, 0, 0, 0, 4]); + Assert.Equal(-96, tensor[0, 0, 0, 0, 0, 5]); + tensor.FlattenTo(results); + Assert.Equal(a, results); + + a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; + results = new int[12]; + tensor = a.AsTensorSpan(1, 2, 2, 1, 1, 3); + Assert.Equal(6, tensor.Lengths.Length); + Assert.Equal(1, tensor.Lengths[0]); + Assert.Equal(2, tensor.Lengths[1]); + Assert.Equal(2, tensor.Lengths[2]); + Assert.Equal(1, tensor.Lengths[3]); + Assert.Equal(1, tensor.Lengths[4]); + Assert.Equal(3, tensor.Lengths[5]); + Assert.Equal(6, tensor.Strides.Length); + Assert.Equal(12, tensor.Strides[0]); + Assert.Equal(6, tensor.Strides[1]); + Assert.Equal(3, tensor.Strides[2]); + Assert.Equal(3, tensor.Strides[3]); + Assert.Equal(3, tensor.Strides[4]); + Assert.Equal(1, tensor.Strides[5]); + Assert.Equal(91, tensor[0, 0, 0, 0, 0, 0]); + Assert.Equal(92, tensor[0, 0, 0, 0, 0, 1]); + Assert.Equal(-93, tensor[0, 0, 0, 0, 0, 2]); + Assert.Equal(94, tensor[0, 0, 1, 0, 0, 0]); + Assert.Equal(95, tensor[0, 0, 1, 0, 0, 1]); + Assert.Equal(-96, tensor[0, 0, 1, 0, 0, 2]); + Assert.Equal(-91, tensor[0, 1, 0, 0, 0, 0]); + Assert.Equal(-92, tensor[0, 1, 0, 0, 0, 1]); + Assert.Equal(93, tensor[0, 1, 0, 0, 0, 2]); + Assert.Equal(-94, tensor[0, 1, 1, 0, 0, 0]); + Assert.Equal(-95, tensor[0, 1, 1, 0, 0, 1]); + Assert.Equal(96, tensor[0, 1, 1, 0, 0, 2]); + tensor.FlattenTo(results); + Assert.Equal(a, results); + } + [Fact] public static void TensorFactoryCreateUninitializedTests() { From fb644875cb83e6935c77aaf1f21347080ec80b37 Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Tue, 11 Jun 2024 15:01:12 -0600 Subject: [PATCH 4/7] removed typo --- src/libraries/System.Numerics.Tensors/tests/TensorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index 3477b10ae0ac25..e9855609b1b6d6 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -263,7 +263,7 @@ public static void TensorLargeDimensionsTests() a = [91, 92, -93, 94, 95, -96, -91, -92, 93, -94, -95, 96]; results = new int[12]; - tensor = a.AsTensorSpan(1, 2, 2, 1, 1, 3); + tensor = Tensor.Create(a, [1, 2, 2, 1, 1, 3]); Assert.Equal(6, tensor.Lengths.Length); Assert.Equal(1, tensor.Lengths[0]); Assert.Equal(2, tensor.Lengths[1]); From 35dc2cf3caa10311c94587ac9eefb8711b37cfec Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Tue, 11 Jun 2024 15:02:42 -0600 Subject: [PATCH 5/7] adding unsaved files --- src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index 7b9ba8f4272e06..e43a7d55e7dfae 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -992,7 +992,7 @@ public static void TensorSpanLargeDimensionsTests() { int[] a = { 91, 92, -93, 94, 95, -96 }; int[] results = new int[6]; - ReadOnlyTensorSpan spanInt = a.AsTensorSpan(1, 1, 1, 1, 1, 6); + TensorSpan spanInt = a.AsTensorSpan(1, 1, 1, 1, 1, 6); Assert.Equal(6, spanInt.Rank); Assert.Equal(6, spanInt.Lengths.Length); From 5053a67cc5b4798371c8ea1102bed992f3aed5df Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Tue, 11 Jun 2024 16:18:17 -0600 Subject: [PATCH 6/7] changes to use const for stack alloc comparison --- .../Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs | 10 +++++----- .../System/Numerics/Tensors/netcore/TensorSpan.cs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs index 625cf5d2c664bf..8e356158cb757f 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs @@ -522,7 +522,7 @@ public void CopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -569,7 +569,7 @@ public bool TryCopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -653,7 +653,7 @@ public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) scoped Span lengths; scoped Span offsets; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { lengths = stackalloc nint[Rank]; offsets = stackalloc nint[Rank]; @@ -692,7 +692,7 @@ public bool TryFlattenTo(scoped Span destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -733,7 +733,7 @@ public void FlattenTo(scoped Span destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index b85c11d29b427b..c64deebe4b3335 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -505,7 +505,7 @@ public void Clear() { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -570,7 +570,7 @@ public void CopyTo(scoped TensorSpan destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -613,7 +613,7 @@ public bool TryCopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -703,7 +703,7 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) scoped Span lengths; scoped Span offsets; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { lengths = stackalloc nint[Rank]; offsets = stackalloc nint[Rank]; @@ -745,7 +745,7 @@ public bool TryFlattenTo(scoped Span destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -786,7 +786,7 @@ public void FlattenTo(scoped Span destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > 5) + if (Rank > TensorSpan.MaxRankForStackAlloc) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; From 63bc01df0920274f74735f93de8129f2c96c9faa Mon Sep 17 00:00:00 2001 From: Michael Sharp Date: Thu, 20 Jun 2024 11:20:43 -0600 Subject: [PATCH 7/7] changes from pr comments --- .../Tensors/netcore/ReadOnlyTensorSpan.cs | 14 +- .../Tensors/netcore/Tensor.Factory.cs | 6 +- .../Tensors/netcore/TensorExtensions.cs | 191 +++++++++--------- .../Numerics/Tensors/netcore/TensorHelpers.cs | 11 +- .../Numerics/Tensors/netcore/TensorShape.cs | 12 +- .../Numerics/Tensors/netcore/TensorSpan.cs | 59 +----- .../Tensors/netcore/TensorSpanHelpers.cs | 1 - 7 files changed, 133 insertions(+), 161 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs index 8e356158cb757f..686cde6cf7b6f5 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs @@ -370,7 +370,7 @@ public ReadOnlyTensorSpan this[params scoped ReadOnlySpan ranges] /// Gets a value indicating whether this is empty. /// /// if this span is empty; otherwise, . - public bool IsEmpty => _shape.FlattenedLength == 0; + public bool IsEmpty => _shape.IsEmpty; /// /// Gets the length of each dimension in this . @@ -522,7 +522,7 @@ public void CopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -569,7 +569,7 @@ public bool TryCopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -653,7 +653,7 @@ public ReadOnlyTensorSpan Slice(params scoped ReadOnlySpan ranges) scoped Span lengths; scoped Span offsets; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { lengths = stackalloc nint[Rank]; offsets = stackalloc nint[Rank]; @@ -692,7 +692,7 @@ public bool TryFlattenTo(scoped Span destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -733,7 +733,7 @@ public void FlattenTo(scoped Span destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -756,7 +756,7 @@ public void FlattenTo(scoped Span destination) { TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); } - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _lengths); + TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); copiedValues += Lengths[Rank - 1]; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs index 7c3309d6ad65d6..afb134a33d8b88 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.Factory.cs @@ -153,16 +153,16 @@ public static Tensor CreateUninitialized(scoped ReadOnlySpan lengths public static ref readonly TensorSpan FillGaussianNormalDistribution(in TensorSpan destination) where T : IFloatingPoint { - Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._flattenedLength); + Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); - GaussianDistribution(span, destination._flattenedLength); + GaussianDistribution(span, destination._shape._memoryLength); return ref destination; } public static ref readonly TensorSpan FillUniformDistribution(in TensorSpan destination) where T : IFloatingPoint { - Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._flattenedLength); + Span span = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); for (int i = 0; i < span.Length; i++) span[i] = T.CreateChecked(Random.Shared.NextDouble()); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs index 94af59482b520c..7c7c093a4fa1a1 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs @@ -128,7 +128,7 @@ public static void BroadcastTo(in this ReadOnlyTensorSpan source, in Tenso internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlySpan shape) { if (input.Lengths.SequenceEqual(shape)) - return new TensorSpan(ref input._reference, shape, input.Strides, input._memoryLength); + return new TensorSpan(ref input._reference, shape, input.Strides, input._shape._memoryLength); if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) ThrowHelper.ThrowArgument_ShapesNotBroadcastCompatible(); @@ -154,7 +154,7 @@ internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlyS } } - TensorSpan output = new TensorSpan(ref input._reference, shape, strides, input._memoryLength); + TensorSpan output = new TensorSpan(ref input._reference, shape, strides, input._shape._memoryLength); return output; } @@ -171,7 +171,7 @@ internal static TensorSpan LazyBroadcast(in TensorSpan input, ReadOnlyS internal static ReadOnlyTensorSpan LazyBroadcast(in ReadOnlyTensorSpan input, ReadOnlySpan shape) { if (input.Lengths.SequenceEqual(shape)) - return new TensorSpan(ref input._reference, shape, input.Strides, input._memoryLength); + return new TensorSpan(ref input._reference, shape, input.Strides, input._shape._memoryLength); if (!TensorHelpers.IsBroadcastableTo(input.Lengths, shape)) ThrowHelper.ThrowArgument_ShapesNotBroadcastCompatible(); @@ -197,7 +197,7 @@ internal static ReadOnlyTensorSpan LazyBroadcast(in ReadOnlyTensorSpan } } - TensorSpan output = new TensorSpan(ref input._reference, shape, strides, input._memoryLength); + TensorSpan output = new TensorSpan(ref input._reference, shape, strides, input._shape._memoryLength); return output; } @@ -364,7 +364,7 @@ public static Tensor ElementwiseEqual(Tensor left, Tensor right) where T : IEqualityOperators { Tensor result; - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { result = Tensor.Create(left.Lengths, false); @@ -418,8 +418,8 @@ public static Tensor ElementwiseEqual(Tensor left, Tensor right) public static ref readonly TensorSpan ElementwiseEqual(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, in TensorSpan destination) where T : IEqualityOperators { - Span result = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); - if (TensorHelpers.AreLengthsTheSame(left, right)) + Span result = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { if (!TensorHelpers.AreLengthsTheSame(destination.Lengths, left.Lengths)) ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(destination)); @@ -499,8 +499,8 @@ public static TensorSpan FilteredUpdate(in this TensorSpan tensor, scop if (filter.Lengths.Length != tensor.Lengths.Length) ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(filter)); - Span srcSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._flattenedLength); - Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._flattenedLength); + Span srcSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); + Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._shape._memoryLength); for (int i = 0; i < filterSpan.Length; i++) { @@ -528,12 +528,12 @@ public static TensorSpan FilteredUpdate(in this TensorSpan tensor, scop ThrowHelper.ThrowArgument_1DTensorRequired(nameof(values)); nint numTrueElements = TensorHelpers.CountTrueElements(filter); - if (numTrueElements != values._flattenedLength) + if (numTrueElements != values._shape._memoryLength) ThrowHelper.ThrowArgument_IncorrectNumberOfFilterItems(nameof(values)); - Span dstSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._flattenedLength); - Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._flattenedLength); - Span valuesSpan = MemoryMarshal.CreateSpan(ref values._reference, (int)values._flattenedLength); + Span dstSpan = MemoryMarshal.CreateSpan(ref tensor._reference, (int)tensor._shape._memoryLength); + Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)tensor._shape._memoryLength); + Span valuesSpan = MemoryMarshal.CreateSpan(ref values._reference, (int)values._shape._memoryLength); int index = 0; for (int i = 0; i < filterSpan.Length; i++) @@ -563,7 +563,7 @@ public static Tensor GreaterThan(Tensor left, Tensor right) where T : IComparisonOperators { Tensor result; - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { result = Tensor.Create(left.Lengths, false); @@ -620,7 +620,7 @@ public static Tensor GreaterThan(Tensor left, T right) { Tensor result = Tensor.Create(left.Lengths, false); - for (int i = 0; i < left.FlattenedLength; i++) + for (int i = 0; i < left._values.Length; i++) { result._values[i] = left._values[i] > right; } @@ -638,7 +638,7 @@ public static Tensor GreaterThan(Tensor left, T right) public static bool GreaterThanAny(Tensor left, Tensor right) where T : IComparisonOperators { - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { for (int i = 0; i < left.FlattenedLength; i++) @@ -691,7 +691,7 @@ public static bool GreaterThanAny(Tensor left, Tensor right) public static bool GreaterThanAll(Tensor left, Tensor right) where T : IComparisonOperators { - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { for (int i = 0; i < left.FlattenedLength; i++) @@ -749,7 +749,7 @@ public static Tensor LessThan(Tensor left, Tensor right) where T : IComparisonOperators { Tensor result; - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { result = Tensor.Create(left.Lengths, false); @@ -806,7 +806,7 @@ public static Tensor LessThan(Tensor left, T right) { Tensor result = Tensor.Create(left.Lengths, false); - for (int i = 0; i < left.FlattenedLength; i++) + for (int i = 0; i < left._values.Length; i++) { result._values[i] = left._values[i] < right; } @@ -824,7 +824,7 @@ public static Tensor LessThan(Tensor left, T right) public static bool LessThanAny(Tensor left, Tensor right) where T : IComparisonOperators { - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { for (int i = 0; i < left.FlattenedLength; i++) { @@ -877,7 +877,7 @@ public static bool LessThanAny(Tensor left, Tensor right) public static bool LessThanAll(Tensor left, Tensor right) where T : IComparisonOperators { - if (TensorHelpers.AreLengthsTheSame(left, right)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { for (int i = 0; i < left.FlattenedLength; i++) { @@ -929,7 +929,7 @@ public static T Mean(scoped in ReadOnlyTensorSpan input) where T : IFloatingPoint { T sum = Sum(input); - return T.CreateChecked(sum / T.CreateChecked(input.FlattenedLength)); + return T.CreateChecked(sum / T.CreateChecked(input._shape._memoryLength)); } #endregion @@ -1079,7 +1079,7 @@ public static TensorSpan Reshape(this in TensorSpan input, params scope if (tempLinear != input.FlattenedLength) ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); nint[] strides = TensorSpanHelpers.CalculateStrides(arrLengths); - TensorSpan output = new TensorSpan(ref input._reference, arrLengths, strides, input._memoryLength); + TensorSpan output = new TensorSpan(ref input._reference, arrLengths, strides, input._shape._memoryLength); return output; } @@ -1114,7 +1114,7 @@ public static ReadOnlyTensorSpan Reshape(this in ReadOnlyTensorSpan inp if (tempLinear != input.FlattenedLength) ThrowHelper.ThrowArgument_InvalidReshapeDimensions(); nint[] strides = TensorSpanHelpers.CalculateStrides(arrLengths); - ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref input._reference, arrLengths, strides, input._memoryLength); + ReadOnlyTensorSpan output = new ReadOnlyTensorSpan(ref input._reference, arrLengths, strides, input._shape._memoryLength); return output; } #endregion @@ -1131,10 +1131,10 @@ public static Tensor Resize(Tensor input, ReadOnlySpan shape) nint newSize = TensorSpanHelpers.CalculateTotalLength(shape); T[] values = input.IsPinned ? GC.AllocateArray((int)newSize) : (new T[newSize]); Tensor output = new Tensor(values, shape, false); - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input.AsTensorSpan()._reference, (int)input.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input.AsTensorSpan()._reference, (int)input._values.Length); Span ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength); - if (newSize > input.FlattenedLength) - TensorSpanHelpers.Memmove(ospan, span, input.FlattenedLength); + if (newSize > input._values.Length) + TensorSpanHelpers.Memmove(ospan, span, input._values.Length); else TensorSpanHelpers.Memmove(ospan, span, newSize); @@ -1149,12 +1149,12 @@ public static Tensor Resize(Tensor input, ReadOnlySpan shape) /// Destination with the desired new shape. public static ref readonly TensorSpan Resize(scoped in ReadOnlyTensorSpan input, in TensorSpan destination) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); - if (destination.FlattenedLength > input.FlattenedLength) - TensorSpanHelpers.Memmove(ospan, span, input.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); + if (destination._shape._memoryLength > input._shape._memoryLength) + TensorSpanHelpers.Memmove(ospan, span, input._shape._memoryLength); else - TensorSpanHelpers.Memmove(ospan, span, input.FlattenedLength); + TensorSpanHelpers.Memmove(ospan, span, destination._shape._memoryLength); return ref destination; } @@ -1186,10 +1186,10 @@ public static ref readonly TensorSpan Reverse(scoped in ReadOnlyTensorSpan { if (axis == -1) { - nint index = input.FlattenedLength - 1; - Span inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span outputSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); - for (int i = 0; i <= input.FlattenedLength / 2; i++) + nint index = input._shape._memoryLength - 1; + Span inputSpan = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span outputSpan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); + for (int i = 0; i <= input._shape._memoryLength / 2; i++) { outputSpan[i] = inputSpan[(int)index]; outputSpan[(int)index--] = inputSpan[i]; @@ -1234,7 +1234,6 @@ public static ref readonly TensorSpan Reverse(scoped in ReadOnlyTensorSpan TensorSpanHelpers.AdjustIndexesDown((int)axis, 1, iIndices, input.Lengths); copiedValues += copyLength; } - //TensorSpanHelpers.Memmove(ref input._reference, ref values[0], input.FlattenedLength); if (oIndicesArray != null && iIndicesArray != null) { @@ -1255,7 +1254,7 @@ public static ref readonly TensorSpan Reverse(scoped in ReadOnlyTensorSpan public static bool SequenceEqual(this scoped in ReadOnlyTensorSpan span, scoped in ReadOnlyTensorSpan other) { return span.FlattenedLength == other.FlattenedLength - && MemoryMarshal.CreateReadOnlySpan(in span.GetPinnableReference(), (int)span.FlattenedLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)other.FlattenedLength)); + && MemoryMarshal.CreateReadOnlySpan(in span.GetPinnableReference(), (int)span._shape._memoryLength).SequenceEqual(MemoryMarshal.CreateReadOnlySpan(in other.GetPinnableReference(), (int)other._shape._memoryLength)); } #endregion @@ -1361,7 +1360,7 @@ public static Tensor[] Split(scoped in ReadOnlyTensorSpan input, nint n { TensorSpanHelpers.Memmove(ref Unsafe.Add(ref oslice._reference, TensorSpanHelpers.ComputeLinearIndex(oIndices, outputs[0].Strides, outputs[0].Lengths)), ref Unsafe.Add(ref islice._reference, TensorSpanHelpers.ComputeLinearIndex(iIndices, islice.Strides, islice.Lengths)), copyLength); TensorSpanHelpers.AdjustIndexes((int)axis, 1, oIndices, outputs[i]._lengths); - TensorSpanHelpers.AdjustIndexes((int)axis - 1, 1, iIndices, input._lengths); + TensorSpanHelpers.AdjustIndexes((int)axis - 1, 1, iIndices, input.Lengths); copiedValues += copyLength; } } @@ -1469,7 +1468,7 @@ public static TensorSpan Squeeze(in this TensorSpan input, int axis = - strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new TensorSpan(ref input._reference, lengths, strides, input._memoryLength); + return new TensorSpan(ref input._reference, lengths, strides, input._shape._memoryLength); } /// @@ -1516,7 +1515,7 @@ public static ReadOnlyTensorSpan Squeeze(in this ReadOnlyTensorSpan inp strides = TensorSpanHelpers.CalculateStrides(lengths); } - return new ReadOnlyTensorSpan(ref input._reference, lengths, strides, input._memoryLength); + return new ReadOnlyTensorSpan(ref input._reference, lengths, strides, input._shape._memoryLength); } #endregion @@ -1560,13 +1559,13 @@ public static T StdDev(in ReadOnlyTensorSpan input) where T : IFloatingPoint, IPowerFunctions, IAdditionOperators, IAdditiveIdentity { T mean = Mean(input); - Span span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); - Span output = new T[input._flattenedLength].AsSpan(); + Span span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span output = new T[input.FlattenedLength]; TensorPrimitives.Subtract(span, mean, output); TensorPrimitives.Abs(output, output); TensorPrimitives.Pow((ReadOnlySpan)output, T.CreateChecked(2), output); T sum = TensorPrimitives.Sum((ReadOnlySpan)output); - return T.CreateChecked(sum / T.CreateChecked(input.FlattenedLength)); + return T.CreateChecked(sum / T.CreateChecked(input._shape._memoryLength)); } #endregion @@ -1604,7 +1603,7 @@ public static string ToString(this in ReadOnlyTensorSpan span, params Read nint copiedValues = 0; T[] values = new T[span.Lengths[span.Rank - 1]]; - while (copiedValues < span._flattenedLength) + while (copiedValues < span.FlattenedLength) { var sp = new ReadOnlyTensorSpan(ref Unsafe.Add(ref span._reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, span.Strides, span.Lengths)), [span.Lengths[span.Rank - 1]], [1], span.Lengths[span.Rank - 1]); sb.Append('{'); @@ -1612,7 +1611,7 @@ public static string ToString(this in ReadOnlyTensorSpan span, params Read sb.Append(string.Join(",", values)); sb.AppendLine("}"); - TensorSpanHelpers.AdjustIndexes(span.Rank - 2, 1, curIndexes, span._lengths); + TensorSpanHelpers.AdjustIndexes(span.Rank - 2, 1, curIndexes, span.Lengths); copiedValues += span.Lengths[span.Rank - 1]; } @@ -1717,11 +1716,11 @@ public static TensorSpan Unsqueeze(in this TensorSpan input, int axis) if (axis < 0) axis = input.Rank - axis; - List tempLengths = input._lengths.ToArray().ToList(); + List tempLengths = input.Lengths.ToArray().ToList(); tempLengths.Insert(axis, 1); nint[] lengths = tempLengths.ToArray(); nint[] strides = TensorSpanHelpers.CalculateStrides(lengths); - return new TensorSpan(ref input._reference, lengths, strides, input._memoryLength); + return new TensorSpan(ref input._reference, lengths, strides, input._shape._memoryLength); } // REVIEW: NAME? NUMPY CALLS THIS expand_dims. @@ -1737,11 +1736,11 @@ public static ReadOnlyTensorSpan Unsqueeze(in this ReadOnlyTensorSpan i if (axis < 0) axis = input.Rank - axis; - List tempLengths = input._lengths.ToArray().ToList(); + List tempLengths = input.Lengths.ToArray().ToList(); tempLengths.Insert(axis, 1); nint[] lengths = tempLengths.ToArray(); nint[] strides = TensorSpanHelpers.CalculateStrides(lengths); - return new ReadOnlyTensorSpan(ref input._reference, lengths, strides, input._memoryLength); + return new ReadOnlyTensorSpan(ref input._reference, lengths, strides, input._shape._memoryLength); } #endregion @@ -2518,7 +2517,7 @@ public static ref readonly TensorSpan CosineSimilarity(scoped in ReadOnlyT if (destination.Lengths[0] != dim1 || destination.Lengths[1] != dim2) ThrowHelper.ThrowArgument_IncompatibleDimensions(left.Lengths[1], right.Lengths[1]); - Span values = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._flattenedLength); + Span values = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); scoped Span leftIndexes = stackalloc nint[2]; scoped Span rightIndexes = stackalloc nint[2]; @@ -2699,8 +2698,11 @@ public static Tensor Divide(Tensor left, Tensor right) public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan input, T val, in TensorSpan destination) where T : IDivisionOperators { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + if (destination._shape._memoryLength < input._shape._memoryLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); TensorPrimitives.Divide(span, val, ospan); return ref destination; } @@ -2714,8 +2716,11 @@ public static ref readonly TensorSpan Divide(scoped in ReadOnlyTensorSpan< public static ref readonly TensorSpan Divide(T val, scoped in ReadOnlyTensorSpan input, in TensorSpan destination) where T : IDivisionOperators { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + if (destination._shape._memoryLength < input._shape._memoryLength) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); TensorPrimitives.Divide(val, span, ospan); return ref destination; } @@ -3012,9 +3017,8 @@ public static ref readonly TensorSpan ILogB(scoped in ReadOnlyTensorSpan /// The input . public static int IndexOfMax(Tensor input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._values.Length); return TensorPrimitives.IndexOfMax(span); } @@ -3022,9 +3026,8 @@ public static int IndexOfMax(Tensor input) /// The input . public static int IndexOfMax(scoped in ReadOnlyTensorSpan input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return TensorPrimitives.IndexOfMax(span); } #endregion @@ -3034,9 +3037,8 @@ public static int IndexOfMax(scoped in ReadOnlyTensorSpan input) /// The input . public static int IndexOfMaxMagnitude(Tensor input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._values.Length); return TensorPrimitives.IndexOfMaxMagnitude(span); } @@ -3044,9 +3046,8 @@ public static int IndexOfMaxMagnitude(Tensor input) /// The input . public static int IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return TensorPrimitives.IndexOfMaxMagnitude(span); } #endregion @@ -3056,9 +3057,8 @@ public static int IndexOfMaxMagnitude(scoped in ReadOnlyTensorSpan input) /// The input . public static int IndexOfMin(Tensor input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._values.Length); return TensorPrimitives.IndexOfMin(span); } @@ -3066,9 +3066,8 @@ public static int IndexOfMin(Tensor input) /// The input . public static int IndexOfMin(scoped in ReadOnlyTensorSpan input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return TensorPrimitives.IndexOfMin(span); } #endregion @@ -3080,9 +3079,8 @@ public static int IndexOfMin(scoped in ReadOnlyTensorSpan input) /// The input . public static int IndexOfMinMagnitude(Tensor input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._values[0], (int)input._values.Length); return TensorPrimitives.IndexOfMinMagnitude(span); } @@ -3092,9 +3090,8 @@ public static int IndexOfMinMagnitude(Tensor input) /// The input . public static int IndexOfMinMagnitude(scoped in ReadOnlyTensorSpan input) where T : INumber - { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return TensorPrimitives.IndexOfMinMagnitude(span); } #endregion @@ -3429,8 +3426,8 @@ public static Tensor Multiply(Tensor left, Tensor right) public static ref readonly TensorSpan Multiply(scoped in ReadOnlyTensorSpan input, T val, in TensorSpan destination) where T : IMultiplyOperators, IMultiplicativeIdentity { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); TensorPrimitives.Multiply(span, val, ospan); return ref destination; } @@ -3489,7 +3486,7 @@ public static T Norm(Tensor input) public static T Norm(scoped in ReadOnlyTensorSpan input) where T : IRootFunctions { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return TensorPrimitives.Norm(span); } #endregion @@ -3841,8 +3838,8 @@ public static Tensor Subtract(Tensor left, Tensor right) public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpan input, T val, in TensorSpan destination) where T : ISubtractionOperators { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); TensorPrimitives.Subtract(span, val, ospan); return ref destination; } @@ -3856,8 +3853,8 @@ public static ref readonly TensorSpan Subtract(scoped in ReadOnlyTensorSpa public static ref readonly TensorSpan Subtract(T val, scoped in ReadOnlyTensorSpan input, in TensorSpan destination) where T : ISubtractionOperators { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); TensorPrimitives.Subtract(val, span, ospan); return ref destination; } @@ -3885,7 +3882,7 @@ public static T Sum(Tensor input) public static T Sum(scoped in ReadOnlyTensorSpan input) where T : IAdditionOperators, IAdditiveIdentity { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return TensorPrimitives.Sum(span); } #endregion @@ -4064,14 +4061,14 @@ private delegate void PerformCalculationTFromSpanInTToSpanOut(ReadOn private static T TensorPrimitivesHelperSpanInTOut(scoped in ReadOnlyTensorSpan input, PerformCalculationSpanInTOut performCalculation) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); return performCalculation(span); } private static ref readonly TensorSpan TensorPrimitivesHelperSpanInIntSpanOut(scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationSpanInIntSpanOut performCalculation) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); - Span data = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span data = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); performCalculation(span, data); return ref destination; } @@ -4079,10 +4076,10 @@ private static ref readonly TensorSpan TensorPrimitivesHelperSpanInIntSpanO private static T TensorPrimitivesHelperTwoSpanInTOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, PerformCalculationTwoSpanInTOut performCalculation) { // If sizes are the same. - if (left.Lengths.SequenceEqual(right.Lengths)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left._shape._memoryLength); + ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right._shape._memoryLength); return performCalculation(span, rspan); } // Broadcasting needs to happen. @@ -4108,16 +4105,16 @@ private static T TensorPrimitivesHelperTwoSpanInTOut(scoped in ReadOnlyTensor private static ref readonly TensorSpan TensorPrimitivesHelperSpanInSpanOut(scoped in ReadOnlyTensorSpan input, in TensorSpan destination, PerformCalculationSpanInSpanOut performCalculation) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); performCalculation(span, ospan); return ref destination; } private static ref readonly TensorSpan TensorPrimitivesHelperSpanInTInSpanOut(scoped in ReadOnlyTensorSpan input, T value, in TensorSpan destination, PerformCalculationSpanInTInSpanOut performCalculation) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); performCalculation(span, value, ospan); return ref destination; } @@ -4126,8 +4123,8 @@ private static ref readonly TensorSpan TensorPrimitivesHelperTFromSpanInTTo where TFrom : IEquatable, IEqualityOperators, INumberBase where TTo : INumberBase { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._flattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._flattenedLength); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref input._reference, (int)input._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); performCalculation(span, ospan); return ref destination; } @@ -4135,11 +4132,13 @@ private static ref readonly TensorSpan TensorPrimitivesHelperTFromSpanInTTo private static ref readonly TensorSpan TensorPrimitivesHelperTwoSpanInSpanOut(scoped in ReadOnlyTensorSpan left, scoped in ReadOnlyTensorSpan right, in TensorSpan destination, PerformCalculationTwoSpanInSpanOut performCalculation) { // If sizes are the same. - if (left.Lengths.SequenceEqual(right.Lengths)) + if (TensorHelpers.AreLengthsTheSame(left, right) && TensorHelpers.IsUnderlyingStorageSameSize(left, right)) { - ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left.FlattenedLength); - ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right.FlattenedLength); - Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination.FlattenedLength); + if (!TensorHelpers.IsUnderlyingStorageSameSize(left, destination)) + ThrowHelper.ThrowArgument_DestinationTooShort(); + ReadOnlySpan span = MemoryMarshal.CreateSpan(ref left._reference, (int)left._shape._memoryLength); + ReadOnlySpan rspan = MemoryMarshal.CreateSpan(ref right._reference, (int)right._shape._memoryLength); + Span ospan = MemoryMarshal.CreateSpan(ref destination._reference, (int)destination._shape._memoryLength); performCalculation(span, rspan, ospan); return ref destination; } @@ -4154,7 +4153,7 @@ private static ref readonly TensorSpan TensorPrimitivesHelperTwoSpanInSpanOut ReadOnlyTensorSpan broadcastedLeft = Tensor.LazyBroadcast(left, newSize); ReadOnlyTensorSpan broadcastedRight = Tensor.LazyBroadcast(right, newSize); - if (!destination.Lengths.SequenceEqual(newSize)) + if (!destination.Lengths.SequenceEqual(newSize) || destination._shape._memoryLength < broadcastedLeft.FlattenedLength) ThrowHelper.ThrowArgument_ShapesNotBroadcastCompatible(); nint rowLength = newSize[^1]; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs index 15428fb9ab280d..76e541a25be1cf 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs @@ -17,7 +17,7 @@ internal static class TensorHelpers /// How many boolean values are true. public static nint CountTrueElements(scoped in ReadOnlyTensorSpan filter) { - Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)filter._flattenedLength); + Span filterSpan = MemoryMarshal.CreateSpan(ref filter._reference, (int)filter._shape._memoryLength); nint count = 0; for (int i = 0; i < filterSpan.Length; i++) { @@ -83,11 +83,14 @@ internal static nint[] GetIntermediateShape(ReadOnlySpan shape1, int shape return newShape; } + internal static bool IsUnderlyingStorageSameSize(scoped in ReadOnlyTensorSpan tensor1, scoped in ReadOnlyTensorSpan tensor2) + => tensor1._shape._memoryLength == tensor2._shape._memoryLength; + internal static bool IsUnderlyingStorageSameSize(Tensor tensor1, Tensor tensor2) - => tensor1.Lengths.Length == tensor2.Lengths.Length; + => tensor1._values.Length == tensor2._values.Length; - internal static bool AreLengthsTheSame(ReadOnlyTensorSpan tensor1, ReadOnlyTensorSpan tensor2) - => tensor1._lengths.SequenceEqual(tensor2._lengths); + internal static bool AreLengthsTheSame(scoped in ReadOnlyTensorSpan tensor1, scoped in ReadOnlyTensorSpan tensor2) + => tensor1.Lengths.SequenceEqual(tensor2.Lengths); internal static bool AreLengthsTheSame(ReadOnlySpan lengths1, ReadOnlySpan lengths2) => lengths1.SequenceEqual(lengths2); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 549aee71bff107..f3b4380cc3f294 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -10,6 +10,12 @@ namespace System.Numerics.Tensors { internal readonly struct TensorShape { + // Used to determine when we need to allocate a metadata array + public const int MaxInlineArraySize = 5; + + // Used to determine when we can stack alloc for indexing vs when we need to allocate + public const int MaxInlineRank = 8; + internal readonly nint[]? _metadata; // 8 bytes internal readonly nint _memoryLength; // 8 bytes @@ -22,7 +28,7 @@ internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan { _memoryLength = memoryLength; _rank = lengths.Length; - if (lengths.Length > 5) + if (lengths.Length > MaxInlineArraySize) { _metadata = new nint[lengths.Length + strides.Length]; lengths.CopyTo(MemoryMarshal.CreateSpan(ref _metadata[0], lengths.Length)); @@ -35,7 +41,7 @@ internal TensorShape(nint memoryLength, ReadOnlySpan lengths, ReadOnlySpan } } - [InlineArray(5)] // 5x8 bytes (40) + [InlineArray(MaxInlineArraySize)] // 5x8 bytes (40) private struct NintBuffer { public nint e0; @@ -52,5 +58,7 @@ private struct NintBuffer : MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetArrayDataReference(_metadata), _rank * 2).Slice(_rank); public nint FlattenedLength => TensorSpanHelpers.CalculateTotalLength(Lengths); + + public bool IsEmpty => FlattenedLength == 0; } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index c64deebe4b3335..ded021082898e0 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -377,7 +377,7 @@ public TensorSpan this[params scoped ReadOnlySpan ranges] /// Gets a value indicating whether this is empty. /// /// if this span is empty; otherwise, . - public bool IsEmpty => _shape.FlattenedLength == 0; + public bool IsEmpty => _shape.IsEmpty; /// /// Gets the length of each dimension in this . @@ -410,6 +410,7 @@ public TensorSpan this[params scoped ReadOnlySpan ranges] left._shape.FlattenedLength == right._shape.FlattenedLength && left.Rank == right.Rank && left._shape.Lengths.SequenceEqual(right._shape.Lengths) && + left._shape.Strides.SequenceEqual(right._shape.Strides) && Unsafe.AreSame(ref left._reference, ref right._reference); /// @@ -505,7 +506,7 @@ public void Clear() { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -524,10 +525,6 @@ public void Clear() TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); clearedValues += Lengths[Rank - 1]; } - Debug.Assert(clearedValues == _shape.FlattenedLength, "Didn't clear the right amount"); - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); } /// @@ -536,18 +533,7 @@ public void Clear() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Fill(T value) { - Span curIndexes = stackalloc nint[Rank]; - nint filledValues = 0; - // REVIEW: If we track the actual length of the backing data, because FlattenedLength doesn't always equal the actual length, we could use that here to not need to loop. - while (filledValues < _shape.FlattenedLength) - { - TensorSpanHelpers.Fill(ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), (nuint)Lengths[Rank - 1], value); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); - filledValues += Lengths[Rank - 1]; - } - - Debug.Assert(filledValues == _shape.FlattenedLength, "Didn't copy the right amount to the array."); - + MemoryMarshal.CreateSpan(ref _reference, (int)_shape._memoryLength).Fill(value); } /// @@ -565,12 +551,12 @@ public void CopyTo(scoped TensorSpan destination) // Using "if (!TryCopyTo(...))" results in two branches: one for the length // check, and one for the result of TryCopyTo. Since these checks are equivalent, // we can optimize by performing the check once ourselves then calling Memmove directly. - if (_shape.FlattenedLength > destination.FlattenedLength) + if (_shape.FlattenedLength > destination._shape._memoryLength) ThrowHelper.ThrowArgumentException_DestinationTooShort(); scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -613,7 +599,7 @@ public bool TryCopyTo(scoped TensorSpan destination) { scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; @@ -703,7 +689,7 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) scoped Span lengths; scoped Span offsets; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { lengths = stackalloc nint[Rank]; offsets = stackalloc nint[Rank]; @@ -741,32 +727,9 @@ public TensorSpan Slice(params scoped ReadOnlySpan ranges) public bool TryFlattenTo(scoped Span destination) { bool retVal = false; - if (destination.Length < _shape.FlattenedLength) + if (destination.Length <= _shape.FlattenedLength) { - scoped Span curIndexes; - nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) - { - curIndexesArray = ArrayPool.Shared.Rent(Rank); - curIndexes = curIndexesArray; - curIndexes = curIndexes.Slice(0, Rank); - } - else - { - curIndexesArray = null; - curIndexes = stackalloc nint[Rank]; - } - - nint copiedValues = 0; - while (copiedValues < _shape.FlattenedLength) - { - TensorSpanHelpers.Memmove(destination.Slice(checked((int)copiedValues)), ref Unsafe.Add(ref _reference, TensorSpanHelpers.ComputeLinearIndex(curIndexes, Strides, Lengths)), Lengths[Rank - 1]); - TensorSpanHelpers.AdjustIndexes(Rank - 2, 1, curIndexes, _shape.Lengths); - copiedValues += Lengths[Rank - 1]; - } - - if (curIndexesArray != null) - ArrayPool.Shared.Return(curIndexesArray); + FlattenTo(destination); retVal = true; } return retVal; @@ -786,7 +749,7 @@ public void FlattenTo(scoped Span destination) scoped Span curIndexes; nint[]? curIndexesArray; - if (Rank > TensorSpan.MaxRankForStackAlloc) + if (Rank > TensorShape.MaxInlineRank) { curIndexesArray = ArrayPool.Shared.Rent(Rank); curIndexes = curIndexesArray; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs index 041fd3d51df0c6..b726ab37871e80 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpanHelpers.cs @@ -14,7 +14,6 @@ namespace System.Numerics.Tensors { internal static partial class TensorSpanHelpers { - internal static bool AreShapesTheSame(ReadOnlyTensorSpan tensor1, ReadOnlyTensorSpan tensor2) where T : IEquatable, IEqualityOperators => tensor1._shape.Lengths.SequenceEqual(tensor2._shape.Lengths);