Skip to content

Commit 2c290a1

Browse files
authored
Merge pull request #122 from linksplatform/issue-96-b6a6427e
Create benchmark for [[likely]] performance
2 parents 6683cc9 + 2c2b8a5 commit 2c290a1

10 files changed

Lines changed: 1057 additions & 1 deletion

File tree

.codacy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ exclude_paths:
55
- "changelog.d/**"
66
- "scripts/**"
77
- "experiments/**"
8+
- "cpp/build/**"
9+
- "cpp/Platform.Numbers.Benchmarks/**"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
bump: minor
3+
---
4+
5+
### Added
6+
- Factorial implementation in Rust (`math` module) with precomputed lookup table
7+
- Rust benchmarks (Criterion) comparing branch prediction optimization strategies: baseline, cold-path separation, unlikely-first, force-inline, and direct array access
8+
- C++ benchmarks (Google Benchmark) comparing `[[likely]]`/`[[unlikely]]`, `__builtin_expect`, force-inline, separate throw, and direct array access
9+
- C# benchmarks (BenchmarkDotNet) comparing `AggressiveInlining`, `AggressiveOptimization`, `DoesNotReturn`, and generic implementations

cpp/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cmake_minimum_required(VERSION 3.13)
22

33
set(LINKS_PLATFORM_TESTS OFF CACHE BOOL "Whether to compile tests")
4+
set(LINKS_PLATFORM_BENCHMARKS OFF CACHE BOOL "Whether to compile benchmarks")
45
set(LINKS_PLATFORM_EXTRA_FLAGS "" CACHE STRING "Extra compiler flags")
56

67
project(Platform.Numbers CXX)
@@ -17,4 +18,21 @@ if(${LINKS_PLATFORM_TESTS})
1718
target_link_libraries(${PROJECT_NAME}.Tests PRIVATE GTest::gtest)
1819
target_link_libraries(${PROJECT_NAME}.Tests PRIVATE GTest::gtest_main)
1920
target_link_libraries(${PROJECT_NAME}.Tests PRIVATE ${PROJECT_NAME}.Library)
21+
endif()
22+
23+
if(${LINKS_PLATFORM_BENCHMARKS})
24+
include(FetchContent)
25+
FetchContent_Declare(
26+
benchmark
27+
GIT_REPOSITORY https://github.com/google/benchmark.git
28+
GIT_TAG v1.9.1
29+
)
30+
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
31+
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE)
32+
FetchContent_MakeAvailable(benchmark)
33+
34+
add_executable(${PROJECT_NAME}.Benchmarks ${PROJECT_NAME}.Benchmarks/MathBenchmarks.cpp)
35+
set_target_properties(${PROJECT_NAME}.Benchmarks PROPERTIES CXX_STANDARD 20)
36+
target_link_libraries(${PROJECT_NAME}.Benchmarks PRIVATE benchmark::benchmark)
37+
target_link_libraries(${PROJECT_NAME}.Benchmarks PRIVATE ${PROJECT_NAME}.Library)
2038
endif()
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include <benchmark/benchmark.h>
2+
#include <cstdint>
3+
#include <stdexcept>
4+
#include <string>
5+
6+
namespace Platform::Numbers::Benchmarks
7+
{
8+
static constexpr std::uint64_t _factorials[] =
9+
{
10+
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800,
11+
479001600, 6227020800ULL, 87178291200ULL, 1307674368000ULL, 20922789888000ULL,
12+
355687428096000ULL, 6402373705728000ULL, 121645100408832000ULL, 2432902008176640000ULL
13+
};
14+
15+
static constexpr std::uint64_t MaximumFactorialNumber = 20;
16+
static constexpr std::uint64_t FactorialNumber = 19;
17+
18+
[[noreturn]] __attribute__((noinline))
19+
static void ThrowOutOfRange()
20+
{
21+
throw std::out_of_range("Only numbers from 0 to 20 are supported by unsigned integer with 64 bits length.");
22+
}
23+
24+
static std::uint64_t FactorialBaseline(std::uint64_t n)
25+
{
26+
if (n <= MaximumFactorialNumber)
27+
{
28+
return _factorials[n];
29+
}
30+
else
31+
{
32+
throw std::out_of_range("Only numbers from 0 to " + std::to_string(MaximumFactorialNumber) + " are supported by unsigned integer with 64 bits length.");
33+
}
34+
}
35+
36+
static std::uint64_t FactorialWithLikely(std::uint64_t n)
37+
{
38+
if (n <= MaximumFactorialNumber) [[likely]]
39+
{
40+
return _factorials[n];
41+
}
42+
else [[unlikely]]
43+
{
44+
throw std::out_of_range("Only numbers from 0 to " + std::to_string(MaximumFactorialNumber) + " are supported by unsigned integer with 64 bits length.");
45+
}
46+
}
47+
48+
static std::uint64_t FactorialWithLikelyAndSeparateThrow(std::uint64_t n)
49+
{
50+
if (n <= MaximumFactorialNumber) [[likely]]
51+
{
52+
return _factorials[n];
53+
}
54+
else [[unlikely]]
55+
{
56+
ThrowOutOfRange();
57+
}
58+
}
59+
60+
__attribute__((always_inline))
61+
static inline std::uint64_t FactorialWithLikelyAndForceInline(std::uint64_t n)
62+
{
63+
if (n <= MaximumFactorialNumber) [[likely]]
64+
{
65+
return _factorials[n];
66+
}
67+
else [[unlikely]]
68+
{
69+
ThrowOutOfRange();
70+
}
71+
}
72+
73+
static std::uint64_t FactorialWithBuiltinExpect(std::uint64_t n)
74+
{
75+
if (__builtin_expect(n <= MaximumFactorialNumber, 1))
76+
{
77+
return _factorials[n];
78+
}
79+
else
80+
{
81+
ThrowOutOfRange();
82+
}
83+
}
84+
85+
static std::uint64_t FactorialUnlikelyFirst(std::uint64_t n)
86+
{
87+
if (n > MaximumFactorialNumber) [[unlikely]]
88+
{
89+
ThrowOutOfRange();
90+
}
91+
return _factorials[n];
92+
}
93+
94+
static std::uint64_t FactorialWithoutAttributes(std::uint64_t n)
95+
{
96+
if (n <= MaximumFactorialNumber)
97+
{
98+
return _factorials[n];
99+
}
100+
ThrowOutOfRange();
101+
__builtin_unreachable();
102+
}
103+
104+
static std::uint64_t FactorialDirectArrayAccess(std::uint64_t n)
105+
{
106+
return _factorials[n];
107+
}
108+
109+
template <typename T>
110+
static T FactorialGenericWithLikely(T n)
111+
{
112+
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integer type");
113+
if (n <= static_cast<T>(MaximumFactorialNumber)) [[likely]]
114+
{
115+
return static_cast<T>(_factorials[static_cast<std::size_t>(n)]);
116+
}
117+
else [[unlikely]]
118+
{
119+
ThrowOutOfRange();
120+
}
121+
}
122+
123+
static void BM_FactorialBaseline(benchmark::State& state)
124+
{
125+
for (auto _ : state)
126+
{
127+
benchmark::DoNotOptimize(FactorialBaseline(FactorialNumber));
128+
}
129+
}
130+
BENCHMARK(BM_FactorialBaseline);
131+
132+
static void BM_FactorialWithLikely(benchmark::State& state)
133+
{
134+
for (auto _ : state)
135+
{
136+
benchmark::DoNotOptimize(FactorialWithLikely(FactorialNumber));
137+
}
138+
}
139+
BENCHMARK(BM_FactorialWithLikely);
140+
141+
static void BM_FactorialWithLikelyAndSeparateThrow(benchmark::State& state)
142+
{
143+
for (auto _ : state)
144+
{
145+
benchmark::DoNotOptimize(FactorialWithLikelyAndSeparateThrow(FactorialNumber));
146+
}
147+
}
148+
BENCHMARK(BM_FactorialWithLikelyAndSeparateThrow);
149+
150+
static void BM_FactorialWithLikelyAndForceInline(benchmark::State& state)
151+
{
152+
for (auto _ : state)
153+
{
154+
benchmark::DoNotOptimize(FactorialWithLikelyAndForceInline(FactorialNumber));
155+
}
156+
}
157+
BENCHMARK(BM_FactorialWithLikelyAndForceInline);
158+
159+
static void BM_FactorialWithBuiltinExpect(benchmark::State& state)
160+
{
161+
for (auto _ : state)
162+
{
163+
benchmark::DoNotOptimize(FactorialWithBuiltinExpect(FactorialNumber));
164+
}
165+
}
166+
BENCHMARK(BM_FactorialWithBuiltinExpect);
167+
168+
static void BM_FactorialUnlikelyFirst(benchmark::State& state)
169+
{
170+
for (auto _ : state)
171+
{
172+
benchmark::DoNotOptimize(FactorialUnlikelyFirst(FactorialNumber));
173+
}
174+
}
175+
BENCHMARK(BM_FactorialUnlikelyFirst);
176+
177+
static void BM_FactorialWithoutAttributes(benchmark::State& state)
178+
{
179+
for (auto _ : state)
180+
{
181+
benchmark::DoNotOptimize(FactorialWithoutAttributes(FactorialNumber));
182+
}
183+
}
184+
BENCHMARK(BM_FactorialWithoutAttributes);
185+
186+
static void BM_FactorialDirectArrayAccess(benchmark::State& state)
187+
{
188+
for (auto _ : state)
189+
{
190+
benchmark::DoNotOptimize(FactorialDirectArrayAccess(FactorialNumber));
191+
}
192+
}
193+
BENCHMARK(BM_FactorialDirectArrayAccess);
194+
195+
static void BM_FactorialGenericWithLikely(benchmark::State& state)
196+
{
197+
for (auto _ : state)
198+
{
199+
benchmark::DoNotOptimize(FactorialGenericWithLikely<std::uint64_t>(FactorialNumber));
200+
}
201+
}
202+
BENCHMARK(BM_FactorialGenericWithLikely);
203+
}
204+
205+
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)