Skip to content

Commit f016dc4

Browse files
Vectorize Enumerable.Range initialization, take 2 (#87992)
* Vectorize Enumerable.Range initialization * Address PR feedback --------- Co-authored-by: Stephen Toub <[email protected]>
1 parent 7982376 commit f016dc4

File tree

2 files changed

+48
-14
lines changed

2 files changed

+48
-14
lines changed

src/libraries/System.Linq/src/System/Linq/Range.SpeedOpt.cs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Numerics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
58

69
namespace System.Linq
710
{
@@ -16,15 +19,17 @@ public override IEnumerable<TResult> Select<TResult>(Func<int, TResult> selector
1619

1720
public int[] ToArray()
1821
{
19-
int[] array = new int[_end - _start];
20-
Fill(array, _start);
22+
int start = _start;
23+
int[] array = new int[_end - start];
24+
Fill(array, start);
2125
return array;
2226
}
2327

2428
public List<int> ToList()
2529
{
26-
List<int> list = new List<int>(_end - _start);
27-
Fill(SetCountAndGetSpan(list, _end - _start), _start);
30+
(int start, int end) = (_start, _end);
31+
List<int> list = new List<int>(end - start);
32+
Fill(SetCountAndGetSpan(list, end - start), start);
2833
return list;
2934
}
3035

@@ -33,9 +38,33 @@ public void CopyTo(int[] array, int arrayIndex) =>
3338

3439
private static void Fill(Span<int> destination, int value)
3540
{
36-
for (int i = 0; i < destination.Length; i++, value++)
41+
ref int pos = ref MemoryMarshal.GetReference(destination);
42+
ref int end = ref Unsafe.Add(ref pos, destination.Length);
43+
44+
if (Vector.IsHardwareAccelerated &&
45+
Vector<int>.Count <= 8 &&
46+
destination.Length >= Vector<int>.Count)
47+
{
48+
Vector<int> init = new Vector<int>((ReadOnlySpan<int>)new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
49+
Vector<int> current = new Vector<int>(value) + init;
50+
Vector<int> increment = new Vector<int>(Vector<int>.Count);
51+
52+
ref int oneVectorFromEnd = ref Unsafe.Subtract(ref end, Vector<int>.Count);
53+
do
54+
{
55+
current.StoreUnsafe(ref pos);
56+
current += increment;
57+
pos = ref Unsafe.Add(ref pos, Vector<int>.Count);
58+
}
59+
while (!Unsafe.IsAddressGreaterThan(ref pos, ref oneVectorFromEnd));
60+
61+
value = current[0];
62+
}
63+
64+
while (Unsafe.IsAddressLessThan(ref pos, ref end))
3765
{
38-
destination[i] = value;
66+
pos = value++;
67+
pos = ref Unsafe.Add(ref pos, 1);
3968
}
4069
}
4170

src/libraries/System.Linq/tests/RangeTests.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
54
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Text;
8-
using System.Threading.Tasks;
95
using Xunit;
106

117
namespace System.Linq.Tests
@@ -26,11 +22,20 @@ public void Range_ProduceCorrectSequence()
2622
Assert.Equal(100, expected);
2723
}
2824

29-
[Fact]
30-
public void Range_ToArray_ProduceCorrectResult()
25+
public static IEnumerable<object[]> Range_ToArray_ProduceCorrectResult_MemberData()
26+
{
27+
for (int i = 0; i < 64; i++)
28+
{
29+
yield return new object[] { i };
30+
}
31+
}
32+
33+
[Theory]
34+
[MemberData(nameof(Range_ToArray_ProduceCorrectResult_MemberData))]
35+
public void Range_ToArray_ProduceCorrectResult(int length)
3136
{
32-
var array = Enumerable.Range(1, 100).ToArray();
33-
Assert.Equal(100, array.Length);
37+
var array = Enumerable.Range(1, length).ToArray();
38+
Assert.Equal(length, array.Length);
3439
for (var i = 0; i < array.Length; i++)
3540
Assert.Equal(i + 1, array[i]);
3641
}

0 commit comments

Comments
 (0)