Skip to content

Commit 41b16b0

Browse files
authored
Merge pull request #60 from LeeCampbell/LeadingZeroCountBenchmark
Leading zero count benchmark
2 parents a8c2a7e + d34e0a7 commit 41b16b0

5 files changed

Lines changed: 214 additions & 10 deletions

File tree

HdrHistogram.Benchmarking/HdrHistogram.Benchmarking.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="BenchmarkDotNet" Version="0.10.6" />
9+
<PackageReference Include="BenchmarkDotNet" Version="0.10.8" />
1010
</ItemGroup>
1111

1212
<ItemGroup Condition="'$(TargetFramework)' == 'net47'">
1313
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows">
14-
<Version>0.10.6</Version>
14+
<Version>0.10.8</Version>
1515
</PackageReference>
1616
</ItemGroup>
1717

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
3+
namespace HdrHistogram.Benchmarking.LeadingZeroCount
4+
{
5+
/// <summary>
6+
/// Contributed from @BBarry at https://github.com/HdrHistogram/HdrHistogram.NET/issues/42
7+
/// This variation perfoms very well. Similar profile to the "Current Impl".
8+
/// Faster on LegacyJIT/CLR, but much slower on RyuJIT (CLR & Core)
9+
/// </summary>
10+
public static class BBarry32BitIfShiftLookupWith64BitShiftBranch
11+
{
12+
private static readonly int[] Lookup;
13+
14+
static BBarry32BitIfShiftLookupWith64BitShiftBranch()
15+
{
16+
Lookup = new int[256];
17+
for (int i = 1; i < 256; ++i)
18+
{
19+
Lookup[i] = (int)(Math.Log(i) / Math.Log(2));
20+
}
21+
}
22+
public static int GetLeadingZeroCount(long value)
23+
{
24+
//TODO: Test this with just < instead of <=? i.e. const of int.Max+1;
25+
if (value <= int.MaxValue)
26+
return 63 - Log2((int)value);
27+
if (value <= uint.MaxValue)
28+
return 62 - Log2((int)(value >> 1));
29+
return 31 - Log2((int)(value >> 32));
30+
}
31+
32+
private static int Log2(int i)
33+
{
34+
if (i >= 0x1000000) { return Lookup[i >> 24] + 24; }
35+
if (i >= 0x10000) { return Lookup[i >> 16] + 16; }
36+
if (i >= 0x100) { return Lookup[i >> 8] + 8; }
37+
return Lookup[i];
38+
}
39+
}
40+
41+
/// <summary>
42+
/// Contributed from @BBarry at https://github.com/HdrHistogram/HdrHistogram.NET/issues/42
43+
/// This variation perfoms very well. Similar profile to the "Current Impl".
44+
/// Faster on LegacyJIT/CLR, but much slower on RyuJIT (CLR & Core)
45+
/// </summary>
46+
public static class BBarry32BitIfShiftLookupWith64BitShiftBranch_2
47+
{
48+
private static readonly int[] Lookup;
49+
static BBarry32BitIfShiftLookupWith64BitShiftBranch_2()
50+
{
51+
Lookup = new int[256];
52+
for (int i = 1; i < 256; ++i)
53+
{
54+
Lookup[i] = (int)(Math.Log(i) / Math.Log(2));
55+
}
56+
}
57+
public static int GetLeadingZeroCount(long value)
58+
{
59+
if (value <= int.MaxValue)
60+
return 63 - Log2((uint)value);
61+
return 32 - Log2((uint)(value >> 31));
62+
}
63+
64+
private static int Log2(uint i)
65+
{
66+
if (i >= 0x1000000) { return Lookup[i >> 24] + 24; }
67+
if (i >= 0x10000) { return Lookup[i >> 16] + 16; }
68+
if (i >= 0x100) { return Lookup[i >> 8] + 8; }
69+
return Lookup[i];
70+
}
71+
}
72+
73+
/// <summary>
74+
/// Contributed from @BBarry at https://github.com/HdrHistogram/HdrHistogram.NET/issues/42
75+
/// This variation perfoms very well. Similar profile to the "Current Impl".
76+
/// Faster on LegacyJIT/CLR, but much slower on RyuJIT (CLR & Core)
77+
/// This just compares only lessThan operator instead of lessThanEqualTo. #completeness
78+
/// </summary>
79+
public static class BBarry32BitIfShiftLookupWith64BitShiftBranch_3
80+
{
81+
private static readonly int[] Lookup;
82+
private const long IntOverflow = int.MaxValue + 1L;
83+
static BBarry32BitIfShiftLookupWith64BitShiftBranch_3()
84+
{
85+
Lookup = new int[256];
86+
for (int i = 1; i < 256; ++i)
87+
{
88+
Lookup[i] = (int)(Math.Log(i) / Math.Log(2));
89+
}
90+
}
91+
public static int GetLeadingZeroCount(long value)
92+
{
93+
if (value < IntOverflow)
94+
return 63 - Log2((uint)value);
95+
return 32 - Log2((uint)(value >> 31));
96+
}
97+
98+
private const int Bit24Range = 0x1000000 - 1;
99+
private const int Bit16Range = 0x10000 - 1;
100+
private const int Bit8Range = 0x100 - 1;
101+
private static int Log2(uint i)
102+
{
103+
if (i > Bit24Range) { return Lookup[i >> 24] + 24; }
104+
if (i > Bit16Range) { return Lookup[i >> 16] + 16; }
105+
if (i > Bit8Range) { return Lookup[i >> 8] + 8; }
106+
return Lookup[i];
107+
}
108+
}
109+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
3+
namespace HdrHistogram.Benchmarking.LeadingZeroCount
4+
{
5+
/// <summary>
6+
/// Contributed from @BBarry at https://github.com/HdrHistogram/HdrHistogram.NET/issues/42
7+
/// This variation inlines all the shifts.
8+
/// It performs on par on LegacyJIT/CLR but significantly slower on RyuJIT.
9+
/// I assume it is because 7 branches vs the 4 above.
10+
/// </summary>
11+
internal static class BBarryIfShiftLookup
12+
{
13+
private static readonly int[] Lookup;
14+
15+
static BBarryIfShiftLookup()
16+
{
17+
Lookup = new int[256];
18+
for (int i = 1; i < 256; ++i)
19+
{
20+
Lookup[i] = (int)(Math.Log(i) / Math.Log(2));
21+
}
22+
}
23+
24+
public static int GetLeadingZeroCount(long value)
25+
{
26+
if (value >= 0x100000000000000) { return 7 - Lookup[value >> 56]; }
27+
if (value >= 0x1000000000000) { return 15 - Lookup[value >> 48]; }
28+
if (value >= 0x10000000000) { return 23 - Lookup[value >> 40]; }
29+
if (value >= 0x100000000) { return 31 - Lookup[value >> 32]; }
30+
if (value >= 0x1000000) { return 39 - Lookup[value >> 24]; }
31+
if (value >= 0x10000) { return 47 - Lookup[value >> 16]; }
32+
if (value >= 0x100) { return 55 - Lookup[value >> 8]; }
33+
return 63 - Lookup[value];
34+
}
35+
}
36+
}

HdrHistogram.Benchmarking/LeadingZeroCount/LeadingZeroCountBenchmarkBase.cs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,23 @@ namespace HdrHistogram.Benchmarking.LeadingZeroCount
2727
/// </remarks>
2828
public abstract class LeadingZeroCountBenchmarkBase
2929
{
30+
private readonly int _maxBit;
3031
private readonly long[] _testValues;
3132

3233
protected LeadingZeroCountBenchmarkBase(int maxBit)
3334
{
35+
_maxBit = maxBit;
36+
37+
38+
//Create array of +ve numbers in the 'maxBit' bit range (i.e. 32 bit or 64bit)
39+
var expectedData = GenerateTestData(maxBit);
40+
_testValues = expectedData.Select(d => d.Value).ToArray();
41+
}
42+
43+
[BenchmarkDotNet.Attributes.GlobalSetup]
44+
public void OneOffValidationOfImplementations()
45+
{
46+
var expectedData = GenerateTestData(_maxBit);
3447
var functions = new Dictionary<string, Func<long, int>>
3548
{
3649
{"CurrentImpl", Bitwise.NumberOfLeadingZeros},
@@ -41,13 +54,12 @@ protected LeadingZeroCountBenchmarkBase(int maxBit)
4154
{"DeBruijn64BitsBitScanner", LeadingZeroCount.DeBruijn64BitsBitScanner.GetLeadingZeroCount},
4255
{"DeBruijnMultiplication", LeadingZeroCount.DeBruijnMultiplication.GetLeadingZeroCount},
4356
{"DeBruijn128Bits", LeadingZeroCount.DeBruijn128Bits.GetLeadingZeroCount},
57+
{"BBarry32BitIfShiftLookupWith64BitShiftBranch", LeadingZeroCount.BBarry32BitIfShiftLookupWith64BitShiftBranch.GetLeadingZeroCount},
58+
{"BBarry32BitIfShiftLookupWith64BitShiftBranch_2", LeadingZeroCount.BBarry32BitIfShiftLookupWith64BitShiftBranch_2.GetLeadingZeroCount},
59+
{"BBarry32BitIfShiftLookupWith64BitShiftBranch_3", LeadingZeroCount.BBarry32BitIfShiftLookupWith64BitShiftBranch_3.GetLeadingZeroCount},
60+
{"BBarryIfShiftLookup", LeadingZeroCount.BBarryIfShiftLookup.GetLeadingZeroCount},
4461
};
45-
46-
//Create array of +ve numbers in the 'maxBit' bit range (i.e. 32 bit or 64bit)
47-
var expectedData = GenerateTestData(maxBit);
4862
ValidateImplementations(expectedData, functions);
49-
50-
_testValues = expectedData.Select(d => d.Value).ToArray();
5163
}
5264

5365
private static CalculationExpectation[] GenerateTestData(int maxBit)
@@ -157,7 +169,7 @@ public int Debruijn128Bit()
157169
}
158170
return sum;
159171
}
160-
172+
161173
[Benchmark]
162174
public int StringManipulation()
163175
{
@@ -168,7 +180,48 @@ public int StringManipulation()
168180
}
169181
return sum;
170182
}
171-
183+
[Benchmark]
184+
public int BBarry_imp1()
185+
{
186+
var sum = 0;
187+
for (int i = 0; i < _testValues.Length; i++)
188+
{
189+
sum += LeadingZeroCount.BBarry32BitIfShiftLookupWith64BitShiftBranch.GetLeadingZeroCount(_testValues[i]);
190+
}
191+
return sum;
192+
}
193+
[Benchmark]
194+
public int BBarry_imp2()
195+
{
196+
var sum = 0;
197+
for (int i = 0; i < _testValues.Length; i++)
198+
{
199+
sum += LeadingZeroCount.BBarryIfShiftLookup.GetLeadingZeroCount(_testValues[i]);
200+
}
201+
return sum;
202+
}
203+
[Benchmark]
204+
public int BBarry_imp3()
205+
{
206+
var sum = 0;
207+
for (int i = 0; i < _testValues.Length; i++)
208+
{
209+
sum += LeadingZeroCount.BBarry32BitIfShiftLookupWith64BitShiftBranch_2.GetLeadingZeroCount(_testValues[i]);
210+
}
211+
return sum;
212+
}
213+
[Benchmark]
214+
public int BBarry_imp4()
215+
{
216+
var sum = 0;
217+
for (int i = 0; i < _testValues.Length; i++)
218+
{
219+
sum += LeadingZeroCount.BBarry32BitIfShiftLookupWith64BitShiftBranch_3.GetLeadingZeroCount(_testValues[i]);
220+
}
221+
return sum;
222+
}
223+
224+
172225
private class CalculationExpectation
173226
{
174227
public long Value { get; }

build.cmd

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ if [%1]==[] (
77
)
88

99
dotnet restore -v=q
10+
IF %ERRORLEVEL% NEQ 0 GOTO EOF
1011

1112
dotnet build -v=q -c=Release /p:Version=%SemVer%
13+
IF %ERRORLEVEL% NEQ 0 GOTO EOF
1214

1315
dotnet test .\HdrHistogram.UnitTests\HdrHistogram.UnitTests.csproj -v=q --no-build -c=Release
16+
IF %ERRORLEVEL% NEQ 0 GOTO EOF
1417

15-
dotnet pack .\HdrHistogram\HdrHistogram.csproj --no-build --include-symbols -c=Release /p:Version=%SemVer%
18+
dotnet pack .\HdrHistogram\HdrHistogram.csproj --no-build --include-symbols -c=Release /p:Version=%SemVer%
19+
IF %ERRORLEVEL% NEQ 0 GOTO EOF
20+
21+
.\HdrHistogram.Benchmarking\bin\Release\net47\HdrHistogram.Benchmarking.exe *

0 commit comments

Comments
 (0)