Skip to content

Commit 6b051a4

Browse files
committed
Add a few variants of early exits with asm diagnoser
1 parent 6fc8ecb commit 6b051a4

2 files changed

Lines changed: 160 additions & 0 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
1+
using System.Runtime.Intrinsics;
2+
using System.Runtime.Intrinsics.X86;
3+
14
namespace Genbox.FastData.Benchmarks.Benchmarks;
25

6+
[DisassemblyDiagnoser]
37
public class NumericEarlyExitBenchmarks
48
{
59
private int _min = 3;
610
private int _max = 42;
711
private int _value = 7;
12+
private Vector256<int> _simdSet = Vector256.Create(3, 5, 7, 11, 13, 17, 19, 23);
813

914
[Benchmark]public bool ValueRange() => _value < _min || _value > _max;
15+
16+
[Benchmark]public bool ValueRangeReduced() => _value - _min > _max - _min;
17+
18+
[Benchmark]public bool ValueRangeReducedUnsigned() => (uint)(_value - _min) > (uint)(_max - _min);
19+
1020
[Benchmark]public bool ValueBitMask() => (_value & 29) != 0;
21+
22+
[Benchmark]public bool ValueSimd256()
23+
{
24+
Vector256<int> value = Vector256.Create(_value);
25+
Vector256<int> matches = Avx2.CompareEqual(value, _simdSet);
26+
return Avx2.MoveMask(matches.AsByte()) != 0;
27+
}
1128
}

Src/FastData.Benchmarks/Benchmarks/StringEarlyExitBenchmarks.cs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace Genbox.FastData.Benchmarks.Benchmarks;
22

3+
[DisassemblyDiagnoser]
34
public class StringEarlyExitBenchmarks
45
{
56
private string _value = "hello world";
@@ -13,16 +14,158 @@ public class StringEarlyExitBenchmarks
1314
private ulong _stringMask = 0xFF93FF93FF9AFF97UL;
1415

1516
[Benchmark]public bool LengthEqual() => _value.Length != 49;
17+
18+
[Benchmark]public bool LengthEqualOpti()
19+
{
20+
int length = _value.Length;
21+
return length != 49;
22+
}
23+
1624
[Benchmark]public bool LengthRange() => _value.Length < _min || _value.Length > _max;
25+
26+
[Benchmark]public bool LengthRangeOpti()
27+
{
28+
int length = _value.Length;
29+
return (uint)(length - _min) > (uint)(_max - _min);
30+
}
31+
1732
[Benchmark]public bool LengthBitmap() => (_bitset[_value.Length >> 6] & (1UL << ((_value.Length - 1) & 63))) == 0;
33+
34+
[Benchmark]public bool LengthBitmapOpti()
35+
{
36+
int length = _value.Length;
37+
return (_bitset[length >> 6] & (1UL << ((length - 1) & 63))) == 0;
38+
}
39+
1840
[Benchmark]public bool LengthDivisor() => _value.Length % 3 != 0;
41+
42+
[Benchmark]public bool LengthDivisorOpti()
43+
{
44+
int length = _value.Length;
45+
return length % 3 != 0;
46+
}
47+
1948
[Benchmark]public bool CharEqualsFirst() => _value[0] != 'h';
49+
50+
[Benchmark]public bool CharEqualsFirstOpti()
51+
{
52+
string value = _value;
53+
return value.Length == 0 || value[0] != 'h';
54+
}
55+
2056
[Benchmark]public bool CharEqualsLast() => _value[^1] != 'h';
57+
58+
[Benchmark]public bool CharEqualsLastOpti()
59+
{
60+
string value = _value;
61+
int length = value.Length;
62+
return length == 0 || value[length - 1] != 'h';
63+
}
64+
2165
[Benchmark]public bool CharEqualsFirstLast() => _value[0] != 'h' || _value[^1] != 'd';
66+
67+
[Benchmark]public bool CharEqualsFirstLastOpti()
68+
{
69+
string value = _value;
70+
int length = value.Length;
71+
if (length == 0)
72+
return true;
73+
74+
if (value[0] != 'h')
75+
return true;
76+
77+
return value[length - 1] != 'd';
78+
}
79+
2280
[Benchmark]public bool CharRangeFirst() => _value[0] < _min || _value[0] > _max;
81+
82+
[Benchmark]public bool CharRangeFirstOpti()
83+
{
84+
string value = _value;
85+
if (value.Length == 0)
86+
return true;
87+
88+
int ch = value[0];
89+
return (uint)(ch - _min) > (uint)(_max - _min);
90+
}
91+
2392
[Benchmark]public bool CharRangeLast() => _value[^1] < _min || _value[^1] > _max;
93+
94+
[Benchmark]public bool CharRangeLastOpti()
95+
{
96+
string value = _value;
97+
int length = value.Length;
98+
if (length == 0)
99+
return true;
100+
101+
int ch = value[length - 1];
102+
return (uint)(ch - _min) > (uint)(_max - _min);
103+
}
104+
24105
[Benchmark]public bool CharBitmapFirst() => ((1UL << _value[0]) & _firstLow) == 0;
106+
107+
[Benchmark]public bool CharBitmapFirstOpti()
108+
{
109+
string value = _value;
110+
if (value.Length == 0)
111+
return true;
112+
113+
int ch = value[0];
114+
return ((1UL << ch) & _firstLow) == 0;
115+
}
116+
25117
[Benchmark]public bool CharBitmapLast() => ((1UL << _value[^1]) & _lastLow) == 0;
118+
119+
[Benchmark]public bool CharBitmapLastOpti()
120+
{
121+
string value = _value;
122+
int length = value.Length;
123+
if (length == 0)
124+
return true;
125+
126+
int ch = value[length - 1];
127+
return ((1UL << ch) & _lastLow) == 0;
128+
}
129+
26130
[Benchmark]public bool StringBitMask() => ((_value[0] | ((ulong)_value[1] << 16) | ((ulong)_value[2] << 32) | ((ulong)_value[3] << 48)) & _stringMask) != 0;
131+
132+
[Benchmark]public bool StringBitMaskOpti()
133+
{
134+
string value = _value;
135+
if (value.Length < 4)
136+
return true;
137+
138+
ulong packed = value[0] | ((ulong)value[1] << 16) | ((ulong)value[2] << 32) | ((ulong)value[3] << 48);
139+
return (packed & _stringMask) != 0;
140+
}
141+
27142
[Benchmark]public bool PrefixSuffix() => _value.StartsWith(_prefix, StringComparison.Ordinal) && _value.EndsWith(_suffix, StringComparison.Ordinal);
143+
144+
[Benchmark]public bool PrefixSuffixOpti()
145+
{
146+
string value = _value;
147+
string prefix = _prefix;
148+
string suffix = _suffix;
149+
int length = value.Length;
150+
int prefixLength = prefix.Length;
151+
int suffixLength = suffix.Length;
152+
153+
if (length < prefixLength || length < suffixLength)
154+
return false;
155+
156+
for (int i = 0; i < prefixLength; i++)
157+
{
158+
if (value[i] != prefix[i])
159+
return false;
160+
}
161+
162+
int suffixStart = length - suffixLength;
163+
for (int i = 0; i < suffixLength; i++)
164+
{
165+
if (value[suffixStart + i] != suffix[i])
166+
return false;
167+
}
168+
169+
return true;
170+
}
28171
}

0 commit comments

Comments
 (0)