Skip to content

Commit 52ae0e9

Browse files
authored
Stop using LINQ in Tensors (#109415)
Using `IEnumerable<T>` and associated LINQ extensions for numerical processing is not appropriate at this level of the stack. The only remaining use is where the input provided by the user is already an `IEnumerable<T>` and `ToArray` is used to get the required `T[]`, which is fine.
1 parent abee177 commit 52ae0e9

File tree

6 files changed

+161
-61
lines changed

6 files changed

+161
-61
lines changed

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/ReadOnlyTensorSpan.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Buffers;
55
using System.Diagnostics;
66
using System.Diagnostics.CodeAnalysis;
7-
using System.Linq;
87
using System.Runtime.CompilerServices;
98
using System.Runtime.InteropServices;
109
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
@@ -141,7 +140,17 @@ public ReadOnlyTensorSpan(ReadOnlySpan<T> span, scoped ReadOnlySpan<nint> length
141140
/// have a rank of 1 and a length equal to the length of the provided <see cref="Array"/>.
142141
/// </summary>
143142
/// <param name="array">The target array.</param>
144-
public ReadOnlyTensorSpan(Array? array) : this(array, ReadOnlySpan<int>.Empty, array == null ? [0] : (from dim in Enumerable.Range(0, array.Rank) select (nint)array.GetLength(dim)).ToArray(), []) { }
143+
public ReadOnlyTensorSpan(Array? array) :
144+
this(array,
145+
ReadOnlySpan<int>.Empty,
146+
array == null ?
147+
[0] :
148+
TensorSpanHelpers.FillLengths(array.Rank <= TensorShape.MaxInlineRank ?
149+
stackalloc nint[array.Rank] :
150+
new nint[array.Rank], array),
151+
[])
152+
{
153+
}
145154

146155
/// <summary>
147156
/// Creates a new <see cref="ReadOnlyTensorSpan{T}"/> over the provided <see cref="Array"/> using the specified start offsets, lengths, and strides.
@@ -153,7 +162,11 @@ public ReadOnlyTensorSpan(Array? array) : this(array, ReadOnlySpan<int>.Empty, a
153162
public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan<int> start, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
154163
{
155164
if (lengths.IsEmpty && array != null)
156-
lengths = (from dim in Enumerable.Range(0, array.Rank) select (nint)array.GetLength(dim)).ToArray();
165+
{
166+
lengths = TensorSpanHelpers.FillLengths(
167+
array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank],
168+
array);
169+
}
157170

158171
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
159172
if (array == null)
@@ -197,7 +210,11 @@ public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan<int> start, scoped R
197210
public ReadOnlyTensorSpan(Array? array, scoped ReadOnlySpan<NIndex> startIndex, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
198211
{
199212
if (lengths.IsEmpty && array != null)
200-
lengths = (from dim in Enumerable.Range(0, array.Rank) select (nint)array.GetLength(dim)).ToArray();
213+
{
214+
lengths = TensorSpanHelpers.FillLengths(
215+
array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank],
216+
array);
217+
}
201218

202219
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
203220
if (array == null)

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/Tensor.cs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Collections.Generic;
77
using System.ComponentModel;
88
using System.Diagnostics.CodeAnalysis;
9-
using System.Linq;
109
using System.Reflection.Metadata.Ecma335;
1110
using System.Runtime.CompilerServices;
1211
using System.Runtime.InteropServices;
@@ -639,29 +638,18 @@ public override int GetHashCode()
639638
/// <summary>
640639
/// Get a string representation of the tensor.
641640
/// </summary>
642-
private string ToMetadataString()
641+
private void ToMetadataString(StringBuilder sb)
643642
{
644-
var sb = new StringBuilder("[");
643+
sb.Append('[');
645644

646-
int n = Rank;
647-
if (n == 0)
645+
for (int i = 0; i < Rank; i++)
648646
{
649-
sb.Append(']');
647+
sb.Append(Lengths[i]);
648+
if (i + 1 < Rank)
649+
sb.Append('x');
650650
}
651-
else
652-
{
653-
for (int i = 0; i < n; i++)
654-
{
655-
sb.Append(Lengths[i]);
656-
if (i + 1 < n)
657-
sb.Append('x');
658-
}
659-
660-
sb.Append(']');
661-
}
662-
sb.Append($", type = {typeof(T)}, isPinned = {IsPinned}");
663651

664-
return sb.ToString();
652+
sb.Append($"], type = {typeof(T)}, isPinned = {IsPinned}");
665653
}
666654

667655
/// <summary>
@@ -671,12 +659,15 @@ private string ToMetadataString()
671659
/// <returns>A <see cref="string"/> representation of the <see cref="Tensor{T}"/></returns>
672660
public string ToString(params ReadOnlySpan<nint> maximumLengths)
673661
{
674-
if (maximumLengths.Length == 0)
675-
maximumLengths = (from number in Enumerable.Range(0, Rank) select (nint)5).ToArray();
662+
if (maximumLengths.IsEmpty)
663+
{
664+
maximumLengths = Rank <= TensorShape.MaxInlineRank ? stackalloc nint[Rank] : new nint[Rank];
665+
}
666+
676667
var sb = new StringBuilder();
677-
sb.AppendLine(ToMetadataString());
668+
ToMetadataString(sb);
678669
sb.AppendLine("{");
679-
sb.Append(AsTensorSpan().ToString(maximumLengths));
670+
((ReadOnlyTensorSpan<T>)AsTensorSpan()).ToString(sb, maximumLengths);
680671
sb.AppendLine("}");
681672
return sb.ToString();
682673
}

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using System.Linq;
76
using System.Runtime.CompilerServices;
87
using System.Runtime.InteropServices;
98
using System.Text;
@@ -3533,7 +3532,8 @@ public static T StdDev<T>(in ReadOnlyTensorSpan<T> x)
35333532
/// <param name="tensor">The <see cref="TensorSpan{T}"/> you want to represent as a string.</param>
35343533
/// <param name="maximumLengths">Maximum Length of each dimension</param>
35353534
/// <returns>A <see cref="string"/> representation of the <paramref name="tensor"/></returns>
3536-
public static string ToString<T>(this in TensorSpan<T> tensor, params ReadOnlySpan<nint> maximumLengths) => ((ReadOnlyTensorSpan<T>)tensor).ToString(maximumLengths);
3535+
public static string ToString<T>(this in TensorSpan<T> tensor, params ReadOnlySpan<nint> maximumLengths) =>
3536+
((ReadOnlyTensorSpan<T>)tensor).ToString(maximumLengths);
35373537

35383538
/// <summary>
35393539
/// Creates a <see cref="string"/> representation of the <see cref="ReadOnlyTensorSpan{T}"/>."/>
@@ -3543,11 +3543,16 @@ public static T StdDev<T>(in ReadOnlyTensorSpan<T> x)
35433543
/// <param name="maximumLengths">Maximum Length of each dimension</param>
35443544
public static string ToString<T>(this in ReadOnlyTensorSpan<T> tensor, params ReadOnlySpan<nint> maximumLengths)
35453545
{
3546+
StringBuilder sb = new();
3547+
ToString(in tensor, sb, maximumLengths);
3548+
return sb.ToString();
3549+
}
35463550

3551+
internal static void ToString<T>(this in ReadOnlyTensorSpan<T> tensor, StringBuilder sb, params ReadOnlySpan<nint> maximumLengths)
3552+
{
35473553
if (maximumLengths.Length != tensor.Rank)
35483554
ThrowHelper.ThrowArgument_DimensionsNotSame(nameof(tensor));
35493555

3550-
var sb = new StringBuilder();
35513556
scoped Span<nint> curIndexes;
35523557
nint[]? curIndexesArray;
35533558
if (tensor.Rank > 6)
@@ -3579,8 +3584,6 @@ public static string ToString<T>(this in ReadOnlyTensorSpan<T> tensor, params Re
35793584

35803585
if (curIndexesArray != null)
35813586
ArrayPool<nint>.Shared.Return(curIndexesArray);
3582-
3583-
return sb.ToString();
35843587
}
35853588

35863589
/// <summary>
@@ -3602,11 +3605,15 @@ public static Tensor<T> Transpose<T>(Tensor<T> tensor)
36023605
{
36033606
if (tensor.Lengths.Length < 2)
36043607
ThrowHelper.ThrowArgument_TransposeTooFewDimensions();
3605-
int[] dimension = Enumerable.Range(0, tensor.Rank).ToArray();
3608+
3609+
Span<int> dimension = tensor.Rank <= TensorShape.MaxInlineRank ? stackalloc int[tensor.Rank] : new int[tensor.Rank];
3610+
TensorSpanHelpers.FillRange(dimension);
3611+
36063612
int temp = dimension[tensor.Rank - 1];
36073613
dimension[tensor.Rank - 1] = dimension[tensor.Rank - 2];
36083614
dimension[tensor.Rank - 2] = temp;
3609-
return PermuteDimensions(tensor, dimension.AsSpan());
3615+
3616+
return PermuteDimensions(tensor, dimension);
36103617
}
36113618
#endregion
36123619

@@ -3666,15 +3673,28 @@ public static Tensor<T> Unsqueeze<T>(this Tensor<T> tensor, int dimension)
36663673
if (dimension < 0)
36673674
dimension = tensor.Rank - dimension;
36683675

3669-
List<nint> tempLengths = tensor._lengths.ToList();
3670-
tempLengths.Insert(dimension, 1);
3671-
nint[] lengths = [.. tempLengths];
3672-
List<nint> tempStrides = tensor.Strides.ToArray().ToList();
3676+
Span<nint> lengths = tensor._lengths.Length + 1 <= TensorShape.MaxInlineRank ?
3677+
stackalloc nint[tensor._lengths.Length + 1] :
3678+
new nint[tensor._lengths.Length + 1];
3679+
tensor._lengths.AsSpan(0, dimension).CopyTo(lengths);
3680+
tensor._lengths.AsSpan(dimension).CopyTo(lengths.Slice(dimension + 1));
3681+
lengths[dimension] = 1;
3682+
3683+
Span<nint> strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ?
3684+
stackalloc nint[tensor.Strides.Length + 1] :
3685+
new nint[tensor.Strides.Length + 1];
36733686
if (dimension == tensor.Rank)
3674-
tempStrides.Add(tensor.Strides[dimension - 1]);
3687+
{
3688+
tensor.Strides.CopyTo(strides);
3689+
strides[dimension] = tensor.Strides[dimension - 1];
3690+
}
36753691
else
3676-
tempStrides.Insert(dimension, tensor.Strides[dimension] * tensor.Lengths[dimension]);
3677-
nint[] strides = [.. tempStrides];
3692+
{
3693+
tensor.Strides.Slice(0, dimension).CopyTo(strides);
3694+
tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1));
3695+
strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension];
3696+
}
3697+
36783698
return new Tensor<T>(tensor._values, lengths, strides);
36793699
}
36803700

@@ -3690,15 +3710,28 @@ public static TensorSpan<T> Unsqueeze<T>(in this TensorSpan<T> tensor, int dimen
36903710
if (dimension < 0)
36913711
dimension = tensor.Rank - dimension;
36923712

3693-
List<nint> tempLengths = tensor.Lengths.ToArray().ToList();
3694-
tempLengths.Insert(dimension, 1);
3695-
nint[] lengths = [.. tempLengths];
3696-
List<nint> tempStrides = tensor.Strides.ToArray().ToList();
3713+
Span<nint> lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ?
3714+
stackalloc nint[tensor.Lengths.Length + 1] :
3715+
new nint[tensor.Lengths.Length + 1];
3716+
tensor.Lengths.Slice(0, dimension).CopyTo(lengths);
3717+
tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1));
3718+
lengths[dimension] = 1;
3719+
3720+
Span<nint> strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ?
3721+
stackalloc nint[tensor.Strides.Length + 1] :
3722+
new nint[tensor.Strides.Length + 1];
36973723
if (dimension == tensor.Rank)
3698-
tempStrides.Add(tensor.Strides[dimension - 1]);
3724+
{
3725+
tensor.Strides.CopyTo(strides);
3726+
strides[dimension] = tensor.Strides[dimension - 1];
3727+
}
36993728
else
3700-
tempStrides.Insert(dimension, tensor.Strides[dimension] * tensor.Lengths[dimension]);
3701-
nint[] strides = [.. tempStrides];
3729+
{
3730+
tensor.Strides.Slice(0, dimension).CopyTo(strides);
3731+
tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1));
3732+
strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension];
3733+
}
3734+
37023735
return new TensorSpan<T>(ref tensor._reference, lengths, strides, tensor._shape._memoryLength);
37033736
}
37043737

@@ -3714,15 +3747,28 @@ public static ReadOnlyTensorSpan<T> Unsqueeze<T>(in this ReadOnlyTensorSpan<T> t
37143747
if (dimension < 0)
37153748
dimension = tensor.Rank - dimension;
37163749

3717-
List<nint> tempLengths = tensor.Lengths.ToArray().ToList();
3718-
tempLengths.Insert(dimension, 1);
3719-
nint[] lengths = [.. tempLengths];
3720-
List<nint> tempStrides = tensor.Strides.ToArray().ToList();
3750+
Span<nint> lengths = tensor.Lengths.Length + 1 <= TensorShape.MaxInlineRank ?
3751+
stackalloc nint[tensor.Lengths.Length + 1] :
3752+
new nint[tensor.Lengths.Length + 1];
3753+
tensor.Lengths.Slice(0, dimension).CopyTo(lengths);
3754+
tensor.Lengths.Slice(dimension).CopyTo(lengths.Slice(dimension + 1));
3755+
lengths[dimension] = 1;
3756+
3757+
Span<nint> strides = tensor.Strides.Length + 1 <= TensorShape.MaxInlineRank ?
3758+
stackalloc nint[tensor.Strides.Length + 1] :
3759+
new nint[tensor.Strides.Length + 1];
37213760
if (dimension == tensor.Rank)
3722-
tempStrides.Add(tensor.Strides[dimension - 1]);
3761+
{
3762+
tensor.Strides.CopyTo(strides);
3763+
strides[dimension] = tensor.Strides[dimension - 1];
3764+
}
37233765
else
3724-
tempStrides.Insert(dimension, tensor.Strides[dimension] * tensor.Lengths[dimension]);
3725-
nint[] strides = [.. tempStrides];
3766+
{
3767+
tensor.Strides.Slice(0, dimension).CopyTo(strides);
3768+
tensor.Strides.Slice(dimension).CopyTo(strides.Slice(dimension + 1));
3769+
strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension];
3770+
}
3771+
37263772
return new ReadOnlyTensorSpan<T>(ref tensor._reference, lengths, strides, tensor._shape._memoryLength);
37273773
}
37283774
#endregion

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorHelpers.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33

44
using System;
55
using System.Diagnostics.CodeAnalysis;
6-
using System.Linq;
76
using System.Runtime.InteropServices;
87

98
namespace System.Numerics.Tensors
109
{
1110
[Experimental(Experimentals.TensorTDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
1211
internal static class TensorHelpers
1312
{
14-
1513
/// <summary>
1614
/// Counts the number of true elements in a boolean filter tensor so we know how much space we will need.
1715
/// </summary>

src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Buffers;
55
using System.Diagnostics;
66
using System.Diagnostics.CodeAnalysis;
7-
using System.Linq;
87
using System.Runtime.CompilerServices;
98
using System.Runtime.InteropServices;
109
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
@@ -144,7 +143,15 @@ public TensorSpan(Span<T> span, scoped ReadOnlySpan<nint> lengths, scoped ReadOn
144143
/// have a rank of 1 and a length equal to the length of the provided <see cref="Array"/>.
145144
/// </summary>
146145
/// <param name="array">The target array.</param>
147-
public TensorSpan(Array? array) : this(array, ReadOnlySpan<int>.Empty, array == null ? [0] : (from dim in Enumerable.Range(0, array.Rank) select (nint)array.GetLength(dim)).ToArray(), []) { }
146+
public TensorSpan(Array? array) :
147+
this(array,
148+
ReadOnlySpan<int>.Empty,
149+
array == null ?
150+
[0] :
151+
TensorSpanHelpers.FillLengths(array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank], array),
152+
[])
153+
{
154+
}
148155

149156
/// <summary>
150157
/// Creates a new <see cref="TensorSpan{T}"/> over the provided <see cref="Array"/> using the specified start offsets, lengths, and strides.
@@ -156,7 +163,11 @@ public TensorSpan(Array? array) : this(array, ReadOnlySpan<int>.Empty, array ==
156163
public TensorSpan(Array? array, scoped ReadOnlySpan<int> start, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
157164
{
158165
if (lengths.IsEmpty && array != null)
159-
lengths = (from dim in Enumerable.Range(0, array.Rank) select (nint)array.GetLength(dim)).ToArray();
166+
{
167+
lengths = TensorSpanHelpers.FillLengths(
168+
array.Rank < TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank],
169+
array);
170+
}
160171

161172
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
162173

@@ -201,7 +212,11 @@ public TensorSpan(Array? array, scoped ReadOnlySpan<int> start, scoped ReadOnlyS
201212
public TensorSpan(Array? array, scoped ReadOnlySpan<NIndex> startIndex, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides)
202213
{
203214
if (lengths.IsEmpty && array != null)
204-
lengths = (from dim in Enumerable.Range(0, array.Rank) select (nint)array.GetLength(dim)).ToArray();
215+
{
216+
lengths = TensorSpanHelpers.FillLengths(
217+
array.Rank <= TensorShape.MaxInlineRank ? stackalloc nint[array.Rank] : new nint[array.Rank],
218+
array);
219+
}
205220

206221
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
207222
strides = strides.IsEmpty ? (ReadOnlySpan<nint>)TensorSpanHelpers.CalculateStrides(lengths, linearLength) : strides;

0 commit comments

Comments
 (0)