Skip to content

Commit

Permalink
Merge 282785f into 3277340
Browse files Browse the repository at this point in the history
  • Loading branch information
neon-sunset committed Jan 8, 2023
2 parents 3277340 + 282785f commit 8b2852a
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 30 deletions.
28 changes: 11 additions & 17 deletions RangeExtensions.Benchmarks/ToListArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,24 @@ namespace RangeExtensions.Benchmarks;
[DisassemblyDiagnoser(maxDepth: 5, exportCombinedDisassemblyReport: true)]
public class ToListArray
{
[Params(10, 1000, 1000000)]
[Params(10, 1000, 100000)]
public int Length;

[Benchmark(Baseline = true)]
public int[] RangeToArray()
{
return (0..Length).ToArray();
}
public int[] RangeToArray() => (0..Length).ToArray();

[Benchmark]
public List<int> RangeToList()
{
return (0..Length).ToList();
}
public int[] EnumerableToArray() => Enumerable.Range(0, Length).ToArray();

[Benchmark]
public int[] EnumerableToArray()
{
return Enumerable.Range(0, Length).ToArray();
}
public List<int> RangeToList() => (0..Length).ToList();

[Benchmark]
public List<int> EnumerableToList()
{
return Enumerable.Range(0, Length).ToList();
}
public List<int> EnumerableToList() => Enumerable.Range(0, Length).ToList();

[Benchmark]
public int[] RangeSelectToArray() => (0..Length).Select(i => i).ToArray();

[Benchmark]
public int[] EnumerableSelectToArray() => Enumerable.Range(0, Length).Select(i => i).ToArray();
}
4 changes: 4 additions & 0 deletions RangeExtensions.Tests/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ public static IEnumerable<int> Indexes(ICollection<int> collection)
yield return 0;
yield return 1;
yield return 2;
yield return 9;
yield return 19;
yield return -1;
yield return -2;
yield return collection.Count / 4;
yield return collection.Count / 3;
yield return collection.Count / 2;
yield return collection.Count - 21;
yield return collection.Count - 11;
yield return collection.Count - 1;
yield return collection.Count;
yield return collection.Count + 1;
Expand Down
100 changes: 89 additions & 11 deletions RangeExtensions/RangeEnumerable.ICollection.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
using System.Diagnostics;
using System.Numerics;

namespace System.Linq;

public readonly partial record struct RangeEnumerable : ICollection<int>
{
#if NETCOREAPP3_1 || NET
// Up to 512bit-wide according to upcoming AVX512 support in .NET 8
private static readonly Vector<int> IncrementMask = new(
(ReadOnlySpan<int>)new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
#endif

public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -31,6 +40,9 @@ public bool Contains(int item)

public void CopyTo(int[] array, int index)
{
#if !NETSTANDARD2_0
CopyTo(array.AsSpan(), index);
#else
if (index < 0 || index > array.Length)
{
ThrowHelpers.ArgumentOutOfRange();
Expand All @@ -42,14 +54,18 @@ public void CopyTo(int[] array, int index)
ThrowHelpers.ArgumentException();
}

var enumerator = GetEnumerator();

for (var i = index; i < count; i++)
if (array.Length is 0 || count is 0)
{
_ = enumerator.MoveNext();
return;
}

array[i] = enumerator.Current;
var i = index;
foreach (var num in this)
{
array[i] = num;
i++;
}
#endif
}

#if !NETSTANDARD2_0
Expand All @@ -66,16 +82,22 @@ public void CopyTo(Span<int> span, int index)
ThrowHelpers.ArgumentException();
}

var enumerator = GetEnumerator();

// TODO: SIMDify because why not
// TODO: Verify that bounds check is elided in codegen
for (var i = index; i < count; i++)
if ((span = span.Slice(index, count)).Length is 0)
{
_ = enumerator.MoveNext();
return;
}

#if NETCOREAPP3_1 || NET
InitializeSpan(_start, _end, span);
return;
#else
var enumerator = GetEnumerator();
for (var i = 0; i < span.Length; i++)
{
enumerator.MoveNext();
span[i] = enumerator.Current;
}
#endif
}
#endif

Expand All @@ -93,4 +115,60 @@ public bool Remove(int item)
{
throw new NotSupportedException();
}

#if NETCOREAPP3_1 || NET
private void InitializeSpan(int start, int end, Span<int> destination)
{
Debug.Assert(start != end);
Debug.Assert(destination.Length != 0 && destination.Length <= Count);

if (destination.Length < Vector<int>.Count * 2)
{
// The caller *must* guarantee that destination length can fit the range
ref var pos = ref destination[0];
foreach (var num in this)
{
pos = num;
pos = ref Unsafe.Add(ref pos, 1);
}
}
else
{
InitializeSpanCore(start, end, destination);
}
}

private void InitializeSpanCore(int start, int end, Span<int> destination)
{
(int shift, start) = start < end
? (1, start)
: (-1, start - 1);

var mask = IncrementMask * shift;
var width = Vector<int>.Count;
var stride = Vector<int>.Count * 2;
var remainder = destination.Length % stride;

ref var pos = ref destination[0];
for (var i = 0; i < destination.Length - remainder; i += stride)
{
var num = start + (i * shift);
var num2 = start + ((i + width) * shift);

var value = new Vector<int>(num) + mask;
var value2 = new Vector<int>(num2) + mask;

ref var dest = ref Unsafe.Add(ref pos, i);
ref var dest2 = ref Unsafe.Add(ref dest, width);

Unsafe.WriteUnaligned(ref Unsafe.As<int, byte>(ref dest), value);
Unsafe.WriteUnaligned(ref Unsafe.As<int, byte>(ref dest2), value2);
}

for (var i = destination.Length - remainder; i < destination.Length; i++)
{
destination[i] = start + (i * shift);
}
}
#endif
}
7 changes: 6 additions & 1 deletion RangeExtensions/RangeEnumerable.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,20 @@ public int[] ToArray()
#else
var array = new int[length];
#endif
var enumerator = GetEnumeratorUnchecked();

#if NETCOREAPP3_1 || NET
InitializeSpan(_start, _end, array);
return array;
#else
var enumerator = GetEnumeratorUnchecked();
for (var i = 0; i < array.Length; i++)
{
enumerator.MoveNext();
array[i] = enumerator.Current;
}

return array;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
2 changes: 1 addition & 1 deletion RangeExtensions/RangeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static int[] ToArray(this Range range)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<int> ToList(this Range range)
{
return range.AsEnumerable().ToList();
return new(range.AsEnumerable());
}

#if !NETSTANDARD2_0
Expand Down

0 comments on commit 8b2852a

Please sign in to comment.