Skip to content

Commit c84a338

Browse files
Add a divlu benchmark
1 parent 68b4bbd commit c84a338

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ if (LIBDIVIDE_BUILD_TESTS)
287287
add_executable(tester test/tester.cpp)
288288
add_executable(test_c99 test/test_c99.c)
289289
add_executable(test_divlu test/test_divlu.c doc/divlu.c)
290+
add_executable(benchmark_divlu test/benchmark_divlu.c doc/divlu.c)
290291
add_executable(fast_div_generator test/fast_div_generator.cpp)
291292
add_executable(benchmark test/benchmark.cpp)
292293
add_executable(benchmark_branchfree test/benchmark_branchfree.cpp)
@@ -300,12 +301,14 @@ if (LIBDIVIDE_BUILD_TESTS)
300301
target_compile_options(tester PRIVATE "${LIBDIVIDE_FLAGS}" "${NO_VECTORIZE}")
301302
target_compile_options(test_c99 PRIVATE "${LIBDIVIDE_FLAGS}" "${NO_VECTORIZE}")
302303
target_compile_options(test_divlu PRIVATE "${LIBDIVIDE_FLAGS}")
304+
target_compile_options(benchmark_divlu PRIVATE "${LIBDIVIDE_FLAGS}" "-Wno-pedantic")
303305
target_compile_options(fast_div_generator PRIVATE "${LIBDIVIDE_FLAGS}" "${NO_VECTORIZE}")
304306
target_compile_options(benchmark PRIVATE "${LIBDIVIDE_FLAGS}" "${NO_VECTORIZE_C}")
305307
target_compile_options(benchmark_branchfree PRIVATE "${LIBDIVIDE_FLAGS}" "${NO_VECTORIZE}")
306308
set_property(TARGET benchmark_branchfree PROPERTY CXX_STANDARD 11)
307309
set_property(TARGET test_c99 PROPERTY C_STANDARD 99)
308310
set_property(TARGET test_divlu PROPERTY C_STANDARD 99)
311+
set_property(TARGET benchmark_divlu PROPERTY C_STANDARD 99)
309312

310313
target_compile_definitions(tester PRIVATE "${LIBDIVIDE_ASSERTIONS}" "${LIBDIVIDE_VECTOR_EXT}")
311314
target_compile_definitions(test_c99 PRIVATE "${LIBDIVIDE_ASSERTIONS}" "${LIBDIVIDE_VECTOR_EXT}")

test/benchmark_divlu.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Benchmark for divlu and divllu.
2+
// Runs continuously until interrupted. All times are ns/call.
3+
// To build standalone:
4+
// cc -O2 -o benchmark_divlu test/benchmark_divlu.c doc/divlu.c
5+
6+
#include <stdint.h>
7+
#include <stdio.h>
8+
#include <time.h>
9+
10+
uint32_t divlu(uint32_t numhi, uint32_t numlo, uint32_t den, uint32_t *r);
11+
uint64_t divllu(uint64_t numhi, uint64_t numlo, uint64_t den, uint64_t *r);
12+
13+
#define ARRAY_LEN 4096
14+
#define NTRIALS 5000
15+
16+
typedef unsigned long long ullong;
17+
18+
static uint64_t now_ns(void) {
19+
struct timespec ts;
20+
clock_gettime(CLOCK_MONOTONIC, &ts);
21+
return (uint64_t)ts.tv_sec * UINT64_C(1000000000) + (uint64_t)ts.tv_nsec;
22+
}
23+
24+
// xorshift64 RNG.
25+
static uint64_t rng = UINT64_C(0x1234567890ABCDEF);
26+
static uint64_t next_rand(void) {
27+
rng ^= rng << 13;
28+
rng ^= rng >> 7;
29+
rng ^= rng << 17;
30+
return rng;
31+
}
32+
33+
static uint32_t numhi32[ARRAY_LEN];
34+
static uint32_t numlo32[ARRAY_LEN];
35+
static uint64_t numhi64[ARRAY_LEN];
36+
static uint64_t numlo64[ARRAY_LEN];
37+
38+
static void fill32(uint32_t den) {
39+
uint32_t i;
40+
for (i = 0; i < ARRAY_LEN; i++) {
41+
numhi32[i] = (uint32_t)(next_rand() % den);
42+
numlo32[i] = (uint32_t)next_rand();
43+
}
44+
}
45+
46+
static void fill64(uint64_t den) {
47+
uint32_t i;
48+
for (i = 0; i < ARRAY_LEN; i++) {
49+
numhi64[i] = next_rand() % den;
50+
numlo64[i] = next_rand();
51+
}
52+
}
53+
54+
// Hardware reference for divlu: a single 64-bit divide.
55+
static uint64_t run_hw64(uint32_t den) {
56+
uint64_t sum = 0;
57+
uint32_t i;
58+
for (i = 0; i < ARRAY_LEN; i++) {
59+
uint64_t num = ((uint64_t)numhi32[i] << 32) | numlo32[i];
60+
sum += num / den;
61+
}
62+
return sum;
63+
}
64+
65+
static uint64_t run_divlu(uint32_t den) {
66+
uint64_t sum = 0;
67+
uint32_t i;
68+
for (i = 0; i < ARRAY_LEN; i++) {
69+
sum += divlu(numhi32[i], numlo32[i], den, NULL);
70+
}
71+
return sum;
72+
}
73+
74+
static uint64_t run_divllu(uint64_t den) {
75+
uint64_t sum = 0;
76+
uint32_t i;
77+
for (i = 0; i < ARRAY_LEN; i++) {
78+
sum += divllu(numhi64[i], numlo64[i], den, NULL);
79+
}
80+
return sum;
81+
}
82+
83+
// Hardware 128/64->64 narrowing divide using __uint128_t.
84+
// clang-cl on Windows does not support 128-bit division (same guard as libdivide.h).
85+
#if defined(__SIZEOF_INT128__) && !(defined(__clang__) && defined(_MSC_VER))
86+
static uint64_t run_hw128(uint64_t den) {
87+
typedef unsigned __int128 u128;
88+
uint64_t sum = 0;
89+
uint32_t i;
90+
for (i = 0; i < ARRAY_LEN; i++) {
91+
u128 num = ((u128)numhi64[i] << 64) | numlo64[i];
92+
sum += (uint64_t)(num / den);
93+
}
94+
return sum;
95+
}
96+
#endif
97+
98+
// Prevent the compiler from discarding results.
99+
static volatile uint64_t sink;
100+
101+
// Time FUNC NTRIALS times, store minimum ns/call in RESULT.
102+
#define TIME_FUNC(result, func) do { \
103+
uint64_t _min_ns = UINT64_MAX; \
104+
int _t; \
105+
for (_t = 0; _t < NTRIALS; _t++) { \
106+
uint64_t _t0 = now_ns(); \
107+
sink = func; \
108+
uint64_t _t1 = now_ns(); \
109+
uint64_t _elapsed = _t1 - _t0; \
110+
if (_elapsed < _min_ns) _min_ns = _elapsed; \
111+
} \
112+
(result) = _min_ns / (double)ARRAY_LEN; \
113+
} while (0)
114+
115+
int main(void) {
116+
uint32_t den = 1;
117+
printf("%10s %10s %10s %10s %10s\n", "den", "divlu", "hw(64b)", "divllu", "hw(128b)");
118+
while (1) {
119+
double t_divlu, t_hw64, t_divllu, t_hw128;
120+
121+
fill32(den);
122+
TIME_FUNC(t_divlu, run_divlu(den));
123+
TIME_FUNC(t_hw64, run_hw64(den));
124+
125+
fill64(den);
126+
TIME_FUNC(t_divllu, run_divllu(den));
127+
#if defined(__SIZEOF_INT128__) && !(defined(__clang__) && defined(_MSC_VER))
128+
TIME_FUNC(t_hw128, run_hw128(den));
129+
#else
130+
t_hw128 = 0;
131+
#endif
132+
133+
printf("%10u %10.3f %10.3f %10.3f %10.3f\n",
134+
den, t_divlu, t_hw64, t_divllu, t_hw128);
135+
fflush(stdout);
136+
137+
den = (den == UINT32_MAX) ? 1 : den + 1;
138+
}
139+
}

0 commit comments

Comments
 (0)