Skip to content

Commit 9399145

Browse files
committed
[First] Add first extension.
1 parent 874d9b9 commit 9399145

File tree

6 files changed

+397
-0
lines changed

6 files changed

+397
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## FirstOnArray
2+
3+
### Source
4+
[FirstOnArray.cs](../../src/StructLinq.Benchmark/FirstOnArray.cs)
5+
6+
### Results:
7+
``` ini
8+
9+
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
10+
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
11+
.NET Core SDK=3.1.403
12+
[Host] : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT
13+
DefaultJob : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT
14+
15+
16+
```
17+
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
18+
|-------------------- |----------:|----------:|----------:|------:|--------:|-------:|------:|------:|----------:|
19+
| Linq | 29.568 ns | 0.4328 ns | 0.3836 ns | 1.00 | 0.00 | - | - | - | - |
20+
| EnumerableLinq | 30.064 ns | 0.3751 ns | 0.3133 ns | 1.02 | 0.02 | - | - | - | - |
21+
| StructLinq | 12.935 ns | 0.1560 ns | 0.1218 ns | 0.44 | 0.01 | 0.0076 | - | - | 32 B |
22+
| StructLinqZeroAlloc | 2.835 ns | 0.0360 ns | 0.0336 ns | 0.10 | 0.00 | - | - | - | - |
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using BenchmarkDotNet.Attributes;
4+
5+
namespace StructLinq.Benchmark
6+
{
7+
[MemoryDiagnoser]
8+
public class FirstOnArray
9+
{
10+
private const int Count = 1000;
11+
private readonly int[] array;
12+
private readonly IEnumerable<int> enumerable;
13+
public FirstOnArray()
14+
{
15+
array = Enumerable.ToArray(Enumerable.Range(0, Count));
16+
enumerable = Enumerable.ToArray(Enumerable.Range(0, Count));
17+
}
18+
19+
[Benchmark(Baseline = true)]
20+
public int Linq() => array.First();
21+
22+
[Benchmark]
23+
public int EnumerableLinq() => enumerable.First();
24+
25+
26+
[Benchmark]
27+
public int StructLinq() => array.ToStructEnumerable().First();
28+
29+
[Benchmark]
30+
public int StructLinqZeroAlloc() => array.ToStructEnumerable().First(x=>x);
31+
32+
}
33+
}

src/StructLinq.Tests/FirstTests.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Linq;
3+
using FluentAssertions;
4+
using Xunit;
5+
6+
namespace StructLinq.Tests
7+
{
8+
public class FirstTests
9+
{
10+
[Fact]
11+
public void ShouldReturnFirstElement()
12+
{
13+
var array = Enumerable.Range(0, 10)
14+
.ToArray()
15+
.ToStructEnumerable()
16+
.First()
17+
.Should()
18+
.Be(0);
19+
}
20+
21+
[Fact]
22+
public void ShouldReturnFirstElementZeroAlloc()
23+
{
24+
var array = Enumerable.Range(0, 10)
25+
.ToArray()
26+
.ToStructEnumerable()
27+
.First(x=>x)
28+
.Should()
29+
.Be(0);
30+
}
31+
32+
[Fact]
33+
public void ShouldThrowException()
34+
{
35+
Assert.Throws<Exception>(() => StructEnumerable.Empty<int>().First());
36+
}
37+
38+
[Fact]
39+
public void ShouldThrowExceptionZeroAlloc()
40+
{
41+
Assert.Throws<Exception>(() => StructEnumerable.Empty<int>().First(x=>x));
42+
}
43+
44+
45+
[Fact]
46+
public void ShouldReturnFirstElementWithFunc()
47+
{
48+
var array = Enumerable.Range(0, 10)
49+
.ToArray()
50+
.ToStructEnumerable()
51+
.First(x=> x > 5)
52+
.Should()
53+
.Be(6);
54+
}
55+
56+
[Fact]
57+
public void ShouldReturnFirstElementWithFuncZeroAlloc()
58+
{
59+
var array = Enumerable.Range(0, 10)
60+
.ToArray()
61+
.ToStructEnumerable()
62+
.First(x=> x > 5, x=> x)
63+
.Should()
64+
.Be(6);
65+
}
66+
}
67+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Linq;
3+
using FluentAssertions;
4+
using Xunit;
5+
6+
namespace StructLinq.Tests
7+
{
8+
public class RefFirstTests
9+
{
10+
[Fact]
11+
public void ShouldReturnFirstElement()
12+
{
13+
var array = Enumerable.Range(0, 10)
14+
.ToArray()
15+
.ToRefStructEnumerable()
16+
.First()
17+
.Should()
18+
.Be(0);
19+
}
20+
21+
[Fact]
22+
public void ShouldReturnFirstElementZeroAlloc()
23+
{
24+
var array = Enumerable.Range(0, 10)
25+
.ToArray()
26+
.ToRefStructEnumerable()
27+
.First(x=>x)
28+
.Should()
29+
.Be(0);
30+
}
31+
32+
[Fact]
33+
public void ShouldThrowException()
34+
{
35+
Assert.Throws<Exception>(() => StructEnumerable.Empty<int>().ToArray().ToRefStructEnumerable().First());
36+
}
37+
38+
[Fact]
39+
public void ShouldThrowExceptionZeroAlloc()
40+
{
41+
Assert.Throws<Exception>(() => StructEnumerable.Empty<int>().ToArray().ToRefStructEnumerable().First(x=>x));
42+
}
43+
44+
45+
[Fact]
46+
public void ShouldReturnFirstElementWithFunc()
47+
{
48+
var array = Enumerable.Range(0, 10)
49+
.ToArray()
50+
.ToRefStructEnumerable()
51+
.First(x=> x > 5)
52+
.Should()
53+
.Be(6);
54+
}
55+
56+
[Fact]
57+
public void ShouldReturnFirstElementWithFuncZeroAlloc()
58+
{
59+
var array = Enumerable.Range(0, 10)
60+
.ToArray()
61+
.ToRefStructEnumerable()
62+
.First(x=> x > 5, x=> x)
63+
.Should()
64+
.Be(6);
65+
}
66+
}
67+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// ReSharper disable once CheckNamespace
2+
3+
using System;
4+
using System.Runtime.CompilerServices;
5+
6+
// ReSharper disable once CheckNamespace
7+
namespace StructLinq
8+
{
9+
public static partial class StructEnumerable
10+
{
11+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
12+
private static ref T RefInnerFirst<T, TEnumerator>(TEnumerator enumerator)
13+
where TEnumerator : struct, IRefStructEnumerator<T>
14+
{
15+
if (enumerator.MoveNext())
16+
{
17+
ref var current = ref enumerator.Current;
18+
enumerator.Dispose();
19+
return ref current;
20+
}
21+
enumerator.Dispose();
22+
throw new Exception("No Elements");
23+
}
24+
25+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
26+
private static ref T RefInnerFirst<T, TEnumerator>(TEnumerator enumerator, Func<T, bool> predicate)
27+
where TEnumerator : struct, IRefStructEnumerator<T>
28+
{
29+
while (enumerator.MoveNext())
30+
{
31+
ref var current = ref enumerator.Current;
32+
if (predicate(current))
33+
{
34+
enumerator.Dispose();
35+
return ref current;
36+
}
37+
}
38+
enumerator.Dispose();
39+
throw new Exception("No Match");
40+
}
41+
42+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
43+
private static ref T RefInnerFirst<T, TEnumerator, TFunc>(TEnumerator enumerator, ref TFunc predicate)
44+
where TEnumerator : struct, IRefStructEnumerator<T>
45+
where TFunc : struct, IInFunction<T, bool>
46+
{
47+
while (enumerator.MoveNext())
48+
{
49+
ref var current = ref enumerator.Current;
50+
if (predicate.Eval(in current))
51+
{
52+
enumerator.Dispose();
53+
return ref current;
54+
}
55+
}
56+
enumerator.Dispose();
57+
throw new Exception("No Match");
58+
}
59+
60+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
61+
public static ref T First<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
62+
where TEnumerator : struct, IRefStructEnumerator<T>
63+
where TEnumerable : IRefStructEnumerable<T, TEnumerator>
64+
{
65+
var enumerator = enumerable.GetEnumerator();
66+
return ref RefInnerFirst<T, TEnumerator>(enumerator);
67+
}
68+
69+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
70+
public static ref T First<T, TEnumerator>(this IRefStructEnumerable<T, TEnumerator> enumerable)
71+
where TEnumerator : struct, IRefStructEnumerator<T>
72+
{
73+
var enumerator = enumerable.GetEnumerator();
74+
return ref RefInnerFirst<T, TEnumerator>(enumerator);
75+
}
76+
77+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
78+
public static ref T First<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, Func<T, bool> predicate, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
79+
where TEnumerator : struct, IRefStructEnumerator<T>
80+
where TEnumerable : IRefStructEnumerable<T, TEnumerator>
81+
{
82+
var enumerator = enumerable.GetEnumerator();
83+
return ref RefInnerFirst(enumerator, predicate);
84+
}
85+
86+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
87+
public static ref T First<T, TEnumerator>(this IRefStructEnumerable<T, TEnumerator> enumerable, Func<T, bool> predicate)
88+
where TEnumerator : struct, IRefStructEnumerator<T>
89+
{
90+
var enumerator = enumerable.GetEnumerator();
91+
return ref RefInnerFirst<T, TEnumerator>(enumerator, predicate);
92+
}
93+
94+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
95+
public static ref T First<T, TEnumerable, TEnumerator, TFunc>(this TEnumerable enumerable, ref TFunc predicate, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
96+
where TEnumerator : struct, IRefStructEnumerator<T>
97+
where TEnumerable : IRefStructEnumerable<T, TEnumerator>
98+
where TFunc : struct, IInFunction<T, bool>
99+
{
100+
var enumerator = enumerable.GetEnumerator();
101+
return ref RefInnerFirst<T, TEnumerator, TFunc>(enumerator, ref predicate);
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)