Skip to content

Commit 7a4839b

Browse files
Tensor<T> - Fixed missing memory offset from ReadOnlyTensorSpan (#113618)
* fixed missing memory offset * comments from pr * more offset testing * fixed new offset check
1 parent 555d2ef commit 7a4839b

File tree

4 files changed

+76
-33
lines changed

4 files changed

+76
-33
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public static Tensor<T> Create<T>(T[] values, scoped ReadOnlySpan<nint> lengths,
6464
/// <exception cref="ArgumentOutOfRangeException"></exception>
6565
public static Tensor<T> Create<T>(T[] values, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides, bool pinned = false)
6666
{
67-
return new Tensor<T>(values, lengths, strides, pinned);
67+
return new Tensor<T>(values, lengths, strides, memoryOffset: 0, pinned);
6868
}
6969

7070
/// <summary>
@@ -77,7 +77,7 @@ public static Tensor<T> Create<T>(T[] values, scoped ReadOnlySpan<nint> lengths,
7777
public static Tensor<T> Create<T>(IEnumerable<T> values, scoped ReadOnlySpan<nint> lengths, bool pinned = false)
7878
{
7979
T[] data = values.ToArray();
80-
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, pinned);
80+
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, memoryOffset: 0, pinned);
8181
}
8282

8383
/// <summary>
@@ -90,7 +90,7 @@ public static Tensor<T> Create<T>(IEnumerable<T> values, scoped ReadOnlySpan<nin
9090
public static Tensor<T> Create<T>(IEnumerable<T> values, scoped ReadOnlySpan<nint> lengths, scoped ReadOnlySpan<nint> strides, bool pinned = false)
9191
{
9292
T[] data = values.ToArray();
93-
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, strides, pinned);
93+
return new Tensor<T>(data, lengths.IsEmpty ? [data.Length] : lengths, strides, memoryOffset: 0, pinned);
9494
}
9595

9696
#region Normal
@@ -115,7 +115,7 @@ public static Tensor<T> CreateAndFillGaussianNormalDistribution<T>(Random random
115115
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
116116
T[] values = new T[linearLength];
117117
GaussianDistribution<T>(values, linearLength, random);
118-
return new Tensor<T>(values, lengths, false);
118+
return new Tensor<T>(values, lengths, memoryOffset: 0, isPinned: false);
119119
}
120120

121121
private static void GaussianDistribution<T>(in Span<T> values, nint linearLength, Random random)
@@ -154,7 +154,7 @@ public static Tensor<T> CreateAndFillUniformDistribution<T>(Random random, param
154154
for (int i = 0; i < values.Length; i++)
155155
values[i] = T.CreateChecked(random.NextDouble());
156156

157-
return new Tensor<T>(values, lengths, false);
157+
return new Tensor<T>(values, lengths, memoryOffset: 0, isPinned: false);
158158
}
159159

160160
/// <summary>
@@ -175,7 +175,7 @@ public static Tensor<T> CreateUninitialized<T>(scoped ReadOnlySpan<nint> lengths
175175
{
176176
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
177177
T[] values = GC.AllocateUninitializedArray<T>((int)linearLength, pinned);
178-
return new Tensor<T>(values, lengths, strides, pinned);
178+
return new Tensor<T>(values, lengths, strides, memoryOffset: 0, pinned);
179179
}
180180

181181
/// <summary>

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

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ internal Tensor()
4949
}
5050

5151
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52-
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, bool isPinned = false, int memoryOffset = 0) : this(values, lengths, Array.Empty<nint>(), isPinned, memoryOffset) { }
52+
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, int memoryOffset, bool isPinned = false) : this(values, lengths, Array.Empty<nint>(), memoryOffset, isPinned) { }
5353

5454
[MethodImpl(MethodImplOptions.AggressiveInlining)]
55-
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> strides, bool isPinned = false, int memoryOffset = 0)
55+
internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> strides, int memoryOffset, bool isPinned = false)
5656
{
5757
if (values == null)
5858
{
@@ -69,6 +69,9 @@ internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> stri
6969
_lengths = lengths.IsEmpty ? [values.Length] : lengths.ToArray();
7070
_memoryOffset = memoryOffset;
7171

72+
if (_memoryOffset < 0 || (_memoryOffset >= values.Length && values.Length != 0 ))
73+
ThrowHelper.ThrowIndexOutOfRangeException();
74+
7275
_flattenedLength = TensorSpanHelpers.CalculateTotalLength(_lengths);
7376
_strides = strides.IsEmpty ? TensorSpanHelpers.CalculateStrides(_lengths, _flattenedLength) : strides.ToArray();
7477
TensorSpanHelpers.ValidateStrides(_strides, _lengths);
@@ -77,12 +80,12 @@ internal Tensor(T[]? values, ReadOnlySpan<nint> lengths, ReadOnlySpan<nint> stri
7780
if (Environment.Is64BitProcess)
7881
{
7982
// See comment in Span<T>.Slice for how this works.
80-
if ((ulong)(uint)maxElements >= (ulong)(uint)values.Length && values.Length != 0)
83+
if ((ulong)(uint)maxElements >= (ulong)(uint)(values.Length - memoryOffset) && values.Length != 0)
8184
ThrowHelper.ThrowArgument_InvalidStridesAndLengths();
8285
}
8386
else
8487
{
85-
if (((uint)maxElements >= (uint)(values.Length)) && values.Length != 0)
88+
if (((uint)maxElements >= (uint)(values.Length - memoryOffset)) && values.Length != 0)
8689
ThrowHelper.ThrowArgument_InvalidStridesAndLengths();
8790
}
8891

@@ -99,7 +102,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.Create(scoped ReadOnlySpan<nint> lengths,
99102
{
100103
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
101104
T[] values = pinned ? GC.AllocateArray<T>((int)linearLength, pinned) : (new T[linearLength]);
102-
return new Tensor<T>(values, lengths.ToArray(), pinned);
105+
return new Tensor<T>(values, lengths.ToArray(), memoryOffset: 0, pinned);
103106
}
104107

105108
/// <summary>
@@ -112,7 +115,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.Create(scoped ReadOnlySpan<nint> lengths,
112115
{
113116
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
114117
T[] values = pinned ? GC.AllocateArray<T>((int)linearLength, pinned) : (new T[linearLength]);
115-
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), pinned);
118+
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), memoryOffset: 0, pinned);
116119
}
117120

118121
/// <summary>
@@ -124,7 +127,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.CreateUninitialized(scoped ReadOnlySpan<n
124127
{
125128
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
126129
T[] values = GC.AllocateUninitializedArray<T>((int)linearLength, pinned);
127-
return new Tensor<T>(values, lengths.ToArray(), pinned);
130+
return new Tensor<T>(values, lengths.ToArray(), memoryOffset: 0, pinned);
128131
}
129132

130133
/// <summary>
@@ -137,7 +140,7 @@ static Tensor<T> ITensor<Tensor<T>, T>.CreateUninitialized(scoped ReadOnlySpan<n
137140
{
138141
nint linearLength = TensorSpanHelpers.CalculateTotalLength(lengths);
139142
T[] values = GC.AllocateUninitializedArray<T>((int)linearLength, pinned);
140-
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), pinned);
143+
return new Tensor<T>(values, lengths.ToArray(), strides.ToArray(), memoryOffset: 0, pinned);
141144
}
142145

143146
// ITensor
@@ -374,24 +377,24 @@ public Tensor<T> this[Tensor<bool> filter]
374377
}
375378
}
376379

377-
return new Tensor<T>(values, [linearLength], _isPinned);
380+
return new Tensor<T>(values, [linearLength], _memoryOffset, _isPinned);
378381
}
379382
}
380383

381384
/// <summary>
382385
/// Defines an implicit conversion of an array to a <see cref="Tensor{T}"/>.
383386
/// </summary>
384-
public static implicit operator Tensor<T>(T[] array) => new Tensor<T>(array, [array.Length]);
387+
public static implicit operator Tensor<T>(T[] array) => new Tensor<T>(array, [array.Length], memoryOffset: 0);
385388

386389
/// <summary>
387390
/// Defines an implicit conversion of a <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/>.
388391
/// </summary>
389-
public static implicit operator TensorSpan<T>(Tensor<T> value) => new TensorSpan<T>(ref MemoryMarshal.GetArrayDataReference(value._values), value._lengths, value._strides, value._flattenedLength);
392+
public static implicit operator TensorSpan<T>(Tensor<T> value) => value.AsTensorSpan();
390393

391394
/// <summary>
392395
/// Defines an implicit conversion of a <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/>.
393396
/// </summary>
394-
public static implicit operator ReadOnlyTensorSpan<T>(Tensor<T> value) => new ReadOnlyTensorSpan<T>(ref MemoryMarshal.GetArrayDataReference(value._values), value._lengths, value._strides, value.FlattenedLength);
397+
public static implicit operator ReadOnlyTensorSpan<T>(Tensor<T> value) => value.AsReadOnlyTensorSpan();
395398

396399
/// <summary>
397400
/// Converts this <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/> pointing to the same backing memory."/>
@@ -411,7 +414,7 @@ public Tensor<T> this[Tensor<bool> filter]
411414
/// </summary>
412415
/// <param name="start">The start location you want in the <see cref="TensorSpan{T}"/>.</param>
413416
/// <returns><see cref="TensorSpan{T}"/> based on the provided ranges.</returns>
414-
public TensorSpan<T> AsTensorSpan(params scoped ReadOnlySpan<nint> start) => Slice(start);
417+
public TensorSpan<T> AsTensorSpan(params scoped ReadOnlySpan<nint> start) => AsTensorSpan().Slice(start);
415418

416419
/// <summary>
417420
/// Converts this <see cref="Tensor{T}"/> to a <see cref="TensorSpan{T}"/> pointing to the same backing memory based on the provided start indexes."/>
@@ -424,7 +427,7 @@ public Tensor<T> this[Tensor<bool> filter]
424427
/// Converts this <see cref="Tensor{T}"/> to a <see cref="ReadOnlyTensorSpan{T}"/> pointing to the same backing memory."/>
425428
/// </summary>
426429
/// <returns><see cref="ReadOnlyTensorSpan{T}"/></returns>
427-
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan<T>(ref MemoryMarshal.GetArrayDataReference(_values), _lengths, _strides, _flattenedLength);
430+
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan() => new ReadOnlyTensorSpan<T>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_values), _memoryOffset), _lengths, _strides, _flattenedLength);
428431

429432
/// <summary>
430433
/// Converts this <see cref="Tensor{T}"/> to a <see cref="ReadOnlyTensorSpan{T}"/> pointing to the same backing memory based on the provided ranges."/>
@@ -438,7 +441,7 @@ public Tensor<T> this[Tensor<bool> filter]
438441
/// </summary>
439442
/// <param name="start">The start locations you want in the <see cref="ReadOnlyTensorSpan{T}"/></param>
440443
/// <returns></returns>
441-
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<nint> start) => Slice(start);
444+
public ReadOnlyTensorSpan<T> AsReadOnlyTensorSpan(params scoped ReadOnlySpan<nint> start) => AsTensorSpan().Slice(start);
442445

443446
/// <summary>
444447
/// Converts this <see cref="Tensor{T}"/> to a <see cref="ReadOnlyTensorSpan{T}"/> pointing to the same backing memory based on the provided start indexes."/>
@@ -515,7 +518,7 @@ public Tensor<T> Slice(params ReadOnlySpan<NRange> start)
515518
if ((memoryOffset >= _values.Length || memoryOffset < 0) && flattenedLength != 0)
516519
ThrowHelper.ThrowIndexOutOfRangeException();
517520

518-
Tensor<T> toReturn = new Tensor<T>(_values, lengths, Strides, _isPinned, memoryOffset);
521+
Tensor<T> toReturn = new Tensor<T>(_values, lengths, Strides, memoryOffset, _isPinned);
519522

520523
if (offsetsArray != null)
521524
ArrayPool<nint>.Shared.Return(offsetsArray);

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ internal static ReadOnlyTensorSpan<T> LazyBroadcast<T>(in ReadOnlyTensorSpan<T>
218218
internal static Tensor<T> LazyBroadcast<T>(Tensor<T> input, ReadOnlySpan<nint> lengths)
219219
{
220220
if (input.Lengths.SequenceEqual(lengths))
221-
return new Tensor<T>(input._values, lengths, false);
221+
return new Tensor<T>(input._values, lengths, input._memoryOffset, isPinned: false);
222222

223223
if (!TensorHelpers.IsBroadcastableTo(input.Lengths, lengths))
224224
ThrowHelper.ThrowArgument_LengthsNotBroadcastCompatible();
@@ -244,7 +244,7 @@ internal static Tensor<T> LazyBroadcast<T>(Tensor<T> input, ReadOnlySpan<nint> l
244244
}
245245
}
246246

247-
Tensor<T> output = new Tensor<T>(input._values, lengths, strides);
247+
Tensor<T> output = new Tensor<T>(input._values, lengths, strides, input._memoryOffset);
248248

249249
return output;
250250
}
@@ -2660,7 +2660,7 @@ public static Tensor<T> PermuteDimensions<T>(this Tensor<T> tensor, params ReadO
26602660
lengths[i] = tensor.Lengths[dimensions[i]];
26612661
permutation = dimensions.ToArray();
26622662
}
2663-
outTensor = new Tensor<T>(values, lengths, Array.Empty<nint>(), tensor._isPinned);
2663+
outTensor = new Tensor<T>(values, lengths, Array.Empty<nint>(), tensor._memoryOffset, tensor._isPinned);
26642664

26652665
ospan = outTensor.AsTensorSpan();
26662666
ispan = tensor.AsTensorSpan();
@@ -2771,7 +2771,7 @@ public static Tensor<T> Reshape<T>(this Tensor<T> tensor, params ReadOnlySpan<ni
27712771
else
27722772
strides = TensorSpanHelpers.CalculateStrides(arrLengths);
27732773

2774-
return new Tensor<T>(tensor._values, arrLengths, strides);
2774+
return new Tensor<T>(tensor._values, arrLengths, strides, tensor._memoryOffset);
27752775
}
27762776

27772777
/// <summary>
@@ -2926,7 +2926,7 @@ public static Tensor<T> Resize<T>(Tensor<T> tensor, ReadOnlySpan<nint> lengths)
29262926
{
29272927
nint newSize = TensorSpanHelpers.CalculateTotalLength(lengths);
29282928
T[] values = tensor.IsPinned ? GC.AllocateArray<T>((int)newSize) : (new T[newSize]);
2929-
Tensor<T> output = new Tensor<T>(values, lengths, false);
2929+
Tensor<T> output = new Tensor<T>(values, lengths, tensor._memoryOffset, isPinned: false);
29302930
ReadOnlySpan<T> span = MemoryMarshal.CreateSpan(ref tensor.AsTensorSpan()._reference, (int)tensor._values.Length);
29312931
Span<T> ospan = MemoryMarshal.CreateSpan(ref output.AsTensorSpan()._reference, (int)output.FlattenedLength);
29322932
if (newSize > tensor._values.Length)
@@ -3217,7 +3217,7 @@ public static Tensor<T>[] Split<T>(scoped in ReadOnlyTensorSpan<T> tensor, int s
32173217
for (int i = 0; i < outputs.Length; i++)
32183218
{
32193219
T[] values = new T[(int)totalToCopy];
3220-
outputs[i] = new Tensor<T>(values, newShape);
3220+
outputs[i] = new Tensor<T>(values, newShape, memoryOffset: 0);
32213221
oIndices.Clear();
32223222
iIndices.Clear();
32233223

@@ -3299,7 +3299,7 @@ public static Tensor<T> SqueezeDimension<T>(this Tensor<T> tensor, int dimension
32993299
strides = TensorSpanHelpers.CalculateStrides(lengths);
33003300
}
33013301

3302-
return new Tensor<T>(tensor._values, lengths, strides);
3302+
return new Tensor<T>(tensor._values, lengths, strides, tensor._memoryOffset);
33033303
}
33043304

33053305
/// <summary>
@@ -3661,7 +3661,7 @@ public static Tensor<T> Unsqueeze<T>(this Tensor<T> tensor, int dimension)
36613661
strides[dimension] = tensor.Strides[dimension] * tensor.Lengths[dimension];
36623662
}
36633663

3664-
return new Tensor<T>(tensor._values, lengths, strides);
3664+
return new Tensor<T>(tensor._values, lengths, strides, tensor._memoryOffset);
36653665
}
36663666

36673667
/// <summary>

src/libraries/System.Numerics.Tensors/tests/TensorTests.cs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,46 @@ public static void TensorStdDevTests()
11361136
Assert.Equal(0f, Tensor.StdDev(upperLeft));
11371137
}
11381138

1139+
[Fact]
1140+
public static void TensorSumTests()
1141+
{
1142+
float[] values = new float[] { 1, 2, 3, 4, 5, 6 };
1143+
Tensor<float> t0 = Tensor.Create<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3]);
1144+
float sum = Tensor.Sum<float>(t0);
1145+
Assert.Equal(21, sum);
1146+
1147+
// Slice first row of 2 x 2 for sum
1148+
Tensor<float> t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(1)), new NRange(new NIndex(0), new NIndex(0, true)));
1149+
sum = Tensor.Sum<float>(t1);
1150+
Assert.Equal(6, sum);
1151+
1152+
// Slice second row of 2 x 2 for sum.
1153+
t1 = t0.Slice(new NRange(new NIndex(1), new NIndex(2)), new NRange(new NIndex(0), new NIndex(0, true)));
1154+
sum = Tensor.Sum<float>(t1);
1155+
Assert.Equal(15, sum);
1156+
1157+
// Slice first column of 2 x 2 for sum.
1158+
t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(2)), new NRange(new NIndex(0), new NIndex(1)));
1159+
sum = Tensor.Sum<float>(t1);
1160+
Assert.Equal(5, sum);
1161+
1162+
// Slice second column of 2 x 2 for sum.
1163+
t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(2)), new NRange(new NIndex(1), new NIndex(2)));
1164+
sum = Tensor.Sum<float>(t1);
1165+
Assert.Equal(7, sum);
1166+
1167+
// Slice Third column of 2 x 2 for sum.
1168+
t1 = t0.Slice(new NRange(new NIndex(0), new NIndex(2)), new NRange(new NIndex(2), new NIndex(3)));
1169+
sum = Tensor.Sum<float>(t1);
1170+
Assert.Equal(9, sum);
1171+
1172+
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], -1));
1173+
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], 100));
1174+
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], int.MinValue));
1175+
Assert.Throws<IndexOutOfRangeException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], int.MaxValue));
1176+
Assert.Throws<ArgumentException>(()=> new Tensor<float>(new float[] { 1, 2, 3, 4, 5, 6 }, [2, 3], 2));
1177+
}
1178+
11391179
public static float StdDev(float[] values)
11401180
{
11411181
float mean = Mean(values);
@@ -2649,7 +2689,7 @@ public void TensorFilteredUpdateTest()
26492689
[Fact]
26502690
public void TensorObjectFillTests()
26512691
{
2652-
ITensor tensor = (ITensor)new Tensor<int>(new int[4], new nint[] { 2, 2 });
2692+
ITensor tensor = (ITensor)new Tensor<int>(new int[4], new nint[] { 2, 2 }, 0);
26532693
tensor.Fill(5);
26542694

26552695
Assert.Equal(5, tensor[0, 0]);
@@ -2670,7 +2710,7 @@ public void TensorObjectFillTests()
26702710
[Fact]
26712711
public void TensorObjectIndexerTests()
26722712
{
2673-
ITensor tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 });
2713+
ITensor tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }, 0);
26742714

26752715
Assert.Equal(1, tensor[new nint[] { 0, 0 }]);
26762716
Assert.Equal(2, tensor[new nint[] { 0, 1 }]);
@@ -2701,7 +2741,7 @@ public void TensorObjectIndexerTests()
27012741
[Fact]
27022742
public void TensorGetPinnedHandleTests()
27032743
{
2704-
Tensor<int> tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 });
2744+
Tensor<int> tensor = new Tensor<int>(new int[] { 1, 2, 3, 4 }, new nint[] { 2, 2 }, 0);
27052745

27062746
using MemoryHandle handle = tensor.GetPinnedHandle();
27072747
unsafe

0 commit comments

Comments
 (0)