Skip to content

Commit 01e8d4c

Browse files
authored
Add a few struct promotion related benchmarks (#2991)
1 parent 6dccc99 commit 01e8d4c

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using BenchmarkDotNet.Attributes;
6+
using MicroBenchmarks;
7+
using System;
8+
using System.Linq;
9+
using System.Runtime.CompilerServices;
10+
11+
namespace Struct
12+
{
13+
[BenchmarkCategory(Categories.Runtime, Categories.JIT)]
14+
public class FilteredSpanEnumerator
15+
{
16+
private int[] _array;
17+
private int[] _inds;
18+
19+
[GlobalSetup]
20+
public void Setup()
21+
{
22+
_array = Enumerable.Range(0, 10000).ToArray();
23+
_inds = Enumerable.Range(0, 10000).ToArray();
24+
}
25+
26+
[Benchmark]
27+
public int Sum()
28+
{
29+
int sum = 0;
30+
foreach (int s in new FilteredSpanEnumerator<int>(_array, _inds))
31+
{
32+
sum += s;
33+
}
34+
35+
return sum;
36+
}
37+
}
38+
39+
public ref struct FilteredSpanEnumerator<T>
40+
{
41+
private readonly ReadOnlySpan<T> arr;
42+
private readonly int[] inds;
43+
44+
private T current;
45+
private int i;
46+
47+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
48+
public FilteredSpanEnumerator(ReadOnlySpan<T> arr, int[] inds) {
49+
this.arr = arr;
50+
this.inds = inds;
51+
current = default;
52+
i = 0;
53+
}
54+
55+
public T Current => current;
56+
57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
public bool MoveNext() {
59+
if (i >= inds.Length)
60+
return false;
61+
62+
current = arr[inds[i++]];
63+
return true;
64+
}
65+
66+
public FilteredSpanEnumerator<T> GetEnumerator() => this;
67+
}
68+
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using BenchmarkDotNet.Attributes;
6+
using MicroBenchmarks;
7+
using System.Collections.Generic;
8+
using System.Collections;
9+
using System.Linq;
10+
11+
namespace Struct
12+
{
13+
[BenchmarkCategory(Categories.Runtime, Categories.JIT)]
14+
public class GSeq
15+
{
16+
private int[] _array;
17+
18+
[GlobalSetup]
19+
public void Setup()
20+
{
21+
_array = Enumerable.Range(0, 10000).ToArray();
22+
}
23+
24+
[Benchmark]
25+
public int FilterSkipMapSum()
26+
{
27+
var arr = new ArrayEnumerator<int>(_array);
28+
var filter = new FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>(arr, new FilterIsOdd());
29+
var skip = new SkipEnumerator<int, FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>>(5, filter);
30+
var map =
31+
new MapEnumerator<int, int,
32+
SkipEnumerator<int, FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>>, Plus15Mapper>(
33+
skip,
34+
new Plus15Mapper());
35+
return Fold2<int, int,
36+
MapEnumerator<int, int,
37+
SkipEnumerator<int, FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>>,
38+
Plus15Mapper>, FoldPlus>(map, 0, new FoldPlus());
39+
}
40+
41+
public static TOut Fold2<TIn, TOut, TEnumerator, TFolder>(TEnumerator enumerator, TOut initial,
42+
TFolder folder)
43+
where TEnumerator : struct, IEnumerator<TIn>
44+
where TFolder : struct, IInvokable<TOut, TIn, TOut>
45+
{
46+
while (enumerator.MoveNext())
47+
initial = folder.Invoke(initial, enumerator.Current);
48+
49+
return initial;
50+
}
51+
}
52+
53+
public interface IInvokable<TIn1, TOut>
54+
{
55+
TOut Invoke(TIn1 value);
56+
}
57+
58+
public interface IInvokable<TIn1, TIn2, TOut>
59+
{
60+
TOut Invoke(TIn1 value1, TIn2 value2);
61+
}
62+
63+
64+
public readonly struct Plus15Mapper : IInvokable<int, int>
65+
{
66+
public int Invoke(int value) => value + 15;
67+
}
68+
69+
public readonly struct FilterIsOdd : IInvokable<int, bool>
70+
{
71+
public bool Invoke(int value) => value % 2 == 1;
72+
}
73+
74+
public readonly struct FoldPlus : IInvokable<int, int, int>
75+
{
76+
public int Invoke(int value1, int value2) => value1 + value2;
77+
}
78+
79+
public struct ArrayEnumerator<T> : IEnumerator<T>
80+
{
81+
private readonly T[] _array;
82+
private int _count;
83+
84+
public ArrayEnumerator(T[] array)
85+
{
86+
_array = array;
87+
_count = -1;
88+
}
89+
90+
91+
public bool MoveNext()
92+
{
93+
_count++;
94+
return (uint)_count < (uint)_array.Length;
95+
}
96+
97+
public void Reset()
98+
{
99+
}
100+
101+
public T Current => _array[_count];
102+
103+
object IEnumerator.Current => Current;
104+
105+
public void Dispose()
106+
{
107+
}
108+
}
109+
110+
public struct SkipEnumerator<T, TEnumerator> : IEnumerator<T>
111+
where TEnumerator : struct, IEnumerator<T>
112+
{
113+
private TEnumerator _enumerator;
114+
private int _skipCount;
115+
116+
public SkipEnumerator(int skipCount, TEnumerator enumerator)
117+
{
118+
_skipCount = skipCount;
119+
_enumerator = enumerator;
120+
}
121+
122+
public bool MoveNext()
123+
{
124+
while (_skipCount > 0)
125+
{
126+
if (!_enumerator.MoveNext())
127+
return false;
128+
_skipCount--;
129+
}
130+
131+
return _enumerator.MoveNext();
132+
}
133+
134+
public void Reset()
135+
{
136+
}
137+
138+
public T Current => _enumerator.Current;
139+
140+
object IEnumerator.Current => Current;
141+
142+
public void Dispose()
143+
{
144+
}
145+
}
146+
147+
public struct MapEnumerator<T, TOut, TEnumerator, TMapper> : IEnumerator<TOut>
148+
where TEnumerator : struct, IEnumerator<T>
149+
where TMapper : struct, IInvokable<T, TOut>
150+
{
151+
private TEnumerator _enumerator;
152+
private TMapper _mapper;
153+
154+
public MapEnumerator(TEnumerator enumerator, TMapper mapper)
155+
{
156+
_enumerator = enumerator;
157+
_mapper = mapper;
158+
}
159+
160+
public bool MoveNext()
161+
{
162+
return _enumerator.MoveNext();
163+
}
164+
165+
public void Reset()
166+
{
167+
}
168+
169+
public TOut Current => _mapper.Invoke(_enumerator.Current);
170+
171+
object IEnumerator.Current => Current;
172+
173+
public void Dispose()
174+
{
175+
}
176+
}
177+
178+
public struct FilterEnumerator<T, TEnumerator, TFilter> : IEnumerator<T>
179+
where TEnumerator : struct, IEnumerator<T>
180+
where TFilter : struct, IInvokable<T, bool>
181+
{
182+
private TEnumerator _enumerator;
183+
private TFilter _filter;
184+
185+
public FilterEnumerator(TEnumerator enumerator, TFilter filter)
186+
{
187+
_enumerator = enumerator;
188+
_filter = filter;
189+
}
190+
191+
public bool MoveNext()
192+
{
193+
while (_enumerator.MoveNext())
194+
{
195+
if (_filter.Invoke(_enumerator.Current))
196+
return true;
197+
}
198+
199+
return false;
200+
}
201+
202+
public void Reset()
203+
{
204+
}
205+
206+
public T Current => _enumerator.Current;
207+
208+
object IEnumerator.Current => Current;
209+
210+
public void Dispose()
211+
{
212+
}
213+
}
214+
215+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using BenchmarkDotNet.Attributes;
6+
using MicroBenchmarks;
7+
using System.Linq;
8+
using System;
9+
using System.Runtime.CompilerServices;
10+
11+
namespace Struct
12+
{
13+
[BenchmarkCategory(Categories.Runtime, Categories.JIT)]
14+
public class SpanWrapper
15+
{
16+
private int[] _array;
17+
18+
[GlobalSetup]
19+
public void Setup()
20+
{
21+
_array = Enumerable.Range(0, 10000).ToArray();
22+
}
23+
24+
[Benchmark]
25+
public int BaselineSum()
26+
{
27+
return SumSpan(_array);
28+
}
29+
30+
[Benchmark]
31+
public int WrapperSum()
32+
{
33+
return SumSpanWrapper(new SpanWrapper<int> { Span = _array });
34+
}
35+
36+
[MethodImpl(MethodImplOptions.NoInlining)]
37+
private int SumSpan(ReadOnlySpan<int> span)
38+
{
39+
int sum = 0;
40+
foreach (int val in span)
41+
sum += val;
42+
43+
return sum;
44+
}
45+
46+
[MethodImpl(MethodImplOptions.NoInlining)]
47+
private int SumSpanWrapper(SpanWrapper<int> spanWrapper)
48+
{
49+
int sum = 0;
50+
foreach (int val in spanWrapper)
51+
sum += val;
52+
53+
return sum;
54+
}
55+
}
56+
57+
public ref struct SpanWrapper<T>
58+
{
59+
public ReadOnlySpan<T> Span;
60+
public ReadOnlySpan<T>.Enumerator GetEnumerator() => Span.GetEnumerator();
61+
}
62+
}

0 commit comments

Comments
 (0)