Skip to content

Commit c452525

Browse files
committed
🎨 Refactor pseudo pext benchmarks to separate algorithms
1 parent 56399d6 commit c452525

File tree

10 files changed

+427
-381
lines changed

10 files changed

+427
-381
lines changed

benchmark/lookup/CMakeLists.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ foreach(BENCH_ALG_NAME ${BENCH_ALG_NAMES})
7777
target_compile_definitions(
7878
${name}
7979
PRIVATE ALG_NAME=bench_${BENCH_ALG_NAME}
80-
QALG_NAME="bench_${BENCH_ALG_NAME}"
81-
BENCH_DATASET=${BENCH_DATASET}
82-
QBENCH_DATASET="${BENCH_DATASET}")
80+
BENCH_DATASET=${BENCH_DATASET} ANKERL_NANOBENCH_IMPLEMENT)
8381
endforeach()
8482
endforeach()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <memory>
5+
6+
inline std::size_t allocated_size = 0;
7+
8+
template <typename T> class TrackingAllocator {
9+
public:
10+
using value_type = T;
11+
12+
TrackingAllocator() noexcept {}
13+
14+
template <typename U>
15+
TrackingAllocator(TrackingAllocator<U> const &) noexcept {}
16+
17+
template <typename U> struct rebind {
18+
using other = TrackingAllocator<U>;
19+
};
20+
21+
T *allocate(std::size_t n) {
22+
T *ptr = std::allocator<T>{}.allocate(n);
23+
allocated_size += (n * sizeof(T));
24+
return ptr;
25+
}
26+
27+
void deallocate(T *p, std::size_t n) {
28+
std::allocator<T>{}.deallocate(p, n);
29+
allocated_size -= (n * sizeof(T));
30+
}
31+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <cstdio>
5+
#include <utility>
6+
7+
#include <frozen/map.h>
8+
#include <nanobench.h>
9+
10+
template <auto data, typename T> constexpr auto make_frozen_map() {
11+
return []<std::size_t... i>(std::index_sequence<i...>) {
12+
return frozen::map<T, T, data.size()>{
13+
{{static_cast<T>(data[i].first),
14+
static_cast<T>(data[i].second)}...}};
15+
}(std::make_index_sequence<data.size()>{});
16+
}
17+
18+
template <auto data, typename T>
19+
__attribute__((noinline, flatten)) T do_frozen_map(T k) {
20+
constexpr static auto map = make_frozen_map<data, T>();
21+
return map.find(k)->second;
22+
}
23+
24+
template <auto data, typename T> void bench_frozen_map(auto name) {
25+
26+
constexpr auto map = make_frozen_map<data, T>();
27+
28+
printf("size: %lu\n", sizeof(map));
29+
30+
do_frozen_map<data, T>(T{});
31+
32+
T k = static_cast<T>(data[0].first);
33+
ankerl::nanobench::Bench().minEpochIterations(2000000).run("chained", [&] {
34+
k = map.find(k)->second;
35+
ankerl::nanobench::doNotOptimizeAway(k);
36+
});
37+
38+
auto i = std::size_t{};
39+
ankerl::nanobench::Bench().minEpochIterations(2000000).run(
40+
"independent", [&] {
41+
auto v = map.find(data[i].first)->second;
42+
i++;
43+
if (i >= data.size()) {
44+
i = 0;
45+
}
46+
ankerl::nanobench::doNotOptimizeAway(v);
47+
});
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <cstdio>
5+
#include <utility>
6+
7+
#include <frozen/unordered_map.h>
8+
#include <nanobench.h>
9+
10+
template <auto data, typename T> constexpr auto make_frozen_unordered_map() {
11+
return []<std::size_t... i>(std::index_sequence<i...>) {
12+
return frozen::unordered_map<T, T, data.size()>{
13+
{{static_cast<T>(data[i].first),
14+
static_cast<T>(data[i].second)}...}};
15+
}(std::make_index_sequence<data.size()>{});
16+
}
17+
18+
template <auto data, typename T>
19+
__attribute__((noinline, flatten)) T do_frozen_unordered_map(T k) {
20+
constexpr static auto map = make_frozen_unordered_map<data, T>();
21+
return map.find(k)->second;
22+
}
23+
24+
template <auto data, typename T> void bench_frozen_unordered_map(auto name) {
25+
26+
constexpr auto map = make_frozen_unordered_map<data, T>();
27+
28+
printf("size: %lu\n", sizeof(map));
29+
30+
do_frozen_unordered_map<data, T>(T{});
31+
32+
T k = static_cast<T>(data[0].first);
33+
ankerl::nanobench::Bench().minEpochIterations(2000000).run("chained", [&] {
34+
k = map.find(k)->second;
35+
ankerl::nanobench::doNotOptimizeAway(k);
36+
});
37+
38+
auto i = std::size_t{};
39+
ankerl::nanobench::Bench().minEpochIterations(2000000).run(
40+
"independent", [&] {
41+
auto v = map.find(data[i].first)->second;
42+
i++;
43+
if (i >= data.size()) {
44+
i = 0;
45+
}
46+
ankerl::nanobench::doNotOptimizeAway(v);
47+
});
48+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#pragma once
2+
3+
#include <lookup/input.hpp>
4+
#include <lookup/pseudo_pext_lookup.hpp>
5+
6+
#include <array>
7+
#include <cstddef>
8+
#include <cstdio>
9+
10+
#include <nanobench.h>
11+
12+
template <auto data, typename T, bool indirect = true,
13+
std::size_t max_search_len = 2>
14+
constexpr auto make_pseudo_pext() {
15+
constexpr static auto input_data = []() {
16+
std::array<lookup::entry<T, T>, data.size()> d{};
17+
18+
for (auto i = std::size_t{}; i < d.size(); i++) {
19+
d[i] = {static_cast<T>(data[i].first),
20+
static_cast<T>(data[i].second)};
21+
}
22+
23+
return d;
24+
}();
25+
26+
constexpr static auto map =
27+
lookup::pseudo_pext_lookup<indirect, max_search_len>::make(
28+
CX_VALUE(lookup::input{0, input_data}));
29+
30+
return map;
31+
}
32+
33+
template <auto data, typename T, bool indirect = true,
34+
std::size_t max_search_len = 2>
35+
__attribute__((noinline, flatten)) T do_pseudo_pext(T k) {
36+
constexpr static auto map =
37+
make_pseudo_pext<data, T, indirect, max_search_len>();
38+
return map[k];
39+
}
40+
41+
template <auto data, typename T, bool indirect = true,
42+
std::size_t max_search_len = 2>
43+
void bench_pseudo_pext(auto name) {
44+
constexpr static auto map =
45+
make_pseudo_pext<data, T, indirect, max_search_len>();
46+
47+
printf("size: %lu\n", sizeof(map));
48+
49+
T k = static_cast<T>(data[0].first);
50+
51+
do_pseudo_pext<data, T, indirect, max_search_len>(k);
52+
ankerl::nanobench::Bench().minEpochIterations(2000000).run("chained", [&] {
53+
k = map[k];
54+
ankerl::nanobench::doNotOptimizeAway(k);
55+
});
56+
57+
auto i = std::size_t{};
58+
ankerl::nanobench::Bench().minEpochIterations(2000000).run(
59+
"independent", [&] {
60+
auto v = map[static_cast<T>(data[i].first)];
61+
i++;
62+
if (i >= data.size()) {
63+
i = 0;
64+
}
65+
ankerl::nanobench::doNotOptimizeAway(v);
66+
});
67+
}
68+
69+
template <auto data, typename T> void bench_pseudo_pext_direct(auto name) {
70+
bench_pseudo_pext<data, T, false, 1>(name);
71+
}
72+
73+
template <auto data, typename T> void bench_pseudo_pext_indirect_1(auto name) {
74+
bench_pseudo_pext<data, T, true, 1>(name);
75+
}
76+
77+
template <auto data, typename T> void bench_pseudo_pext_indirect_2(auto name) {
78+
bench_pseudo_pext<data, T, true, 2>(name);
79+
}
80+
81+
template <auto data, typename T> void bench_pseudo_pext_indirect_3(auto name) {
82+
bench_pseudo_pext<data, T, true, 3>(name);
83+
}
84+
85+
template <auto data, typename T> void bench_pseudo_pext_indirect_4(auto name) {
86+
bench_pseudo_pext<data, T, true, 4>(name);
87+
}
88+
89+
template <auto data, typename T> void bench_pseudo_pext_indirect_5(auto name) {
90+
bench_pseudo_pext<data, T, true, 5>(name);
91+
}
92+
93+
template <auto data, typename T> void bench_pseudo_pext_indirect_6(auto name) {
94+
bench_pseudo_pext<data, T, true, 6>(name);
95+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
3+
#include "allocator.hpp"
4+
5+
#include <cstddef>
6+
#include <cstdio>
7+
#include <functional>
8+
#include <map>
9+
#include <utility>
10+
11+
#include <nanobench.h>
12+
13+
template <auto data, typename T> void bench_std_map(auto name) {
14+
using allocator_t = TrackingAllocator<std::pair<T const, T>>;
15+
auto map = std::map<T, T, std::less<int>, allocator_t>{};
16+
17+
for (auto p : data) {
18+
map[p.first] = p.second;
19+
}
20+
21+
printf("size: %lu\n", sizeof(map) + allocated_size);
22+
23+
T k = static_cast<T>(data[0].first);
24+
ankerl::nanobench::Bench().minEpochIterations(2000000).run("chained", [&] {
25+
k = map[k];
26+
ankerl::nanobench::doNotOptimizeAway(k);
27+
});
28+
29+
auto i = std::size_t{};
30+
ankerl::nanobench::Bench().minEpochIterations(2000000).run(
31+
"independent", [&] {
32+
auto v = map[static_cast<T>(data[i].first)];
33+
i++;
34+
if (i >= data.size()) {
35+
i = 0;
36+
}
37+
ankerl::nanobench::doNotOptimizeAway(v);
38+
});
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include "allocator.hpp"
4+
5+
#include <cstddef>
6+
#include <cstdio>
7+
#include <functional>
8+
#include <unordered_map>
9+
#include <utility>
10+
11+
#include <nanobench.h>
12+
13+
template <auto data, typename T> void bench_std_unordered_map(auto name) {
14+
using allocator_t = TrackingAllocator<std::pair<T const, T>>;
15+
auto map = std::unordered_map<T, T, std::hash<int>, std::equal_to<int>,
16+
allocator_t>{};
17+
18+
for (auto p : data) {
19+
map[p.first] = p.second;
20+
}
21+
22+
printf("size: %lu\n", sizeof(map) + allocated_size);
23+
24+
T k = static_cast<T>(data[0].first);
25+
ankerl::nanobench::Bench().minEpochIterations(2000000).run("chained", [&] {
26+
k = map[k];
27+
ankerl::nanobench::doNotOptimizeAway(k);
28+
});
29+
30+
auto i = std::size_t{};
31+
ankerl::nanobench::Bench().minEpochIterations(2000000).run(
32+
"independent", [&] {
33+
auto v = map[static_cast<T>(data[i].first)];
34+
i++;
35+
if (i >= data.size()) {
36+
i = 0;
37+
}
38+
ankerl::nanobench::doNotOptimizeAway(v);
39+
});
40+
}

benchmark/lookup/mph.hpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#pragma once
2+
3+
#include <array>
4+
#include <cstddef>
5+
#include <functional>
6+
#include <mph>
7+
#include <utility>
8+
9+
#include <nanobench.h>
10+
11+
template <auto data, typename T> constexpr auto make_mph() {
12+
constexpr static auto input_data =
13+
[]<std::size_t... i>(std::index_sequence<i...>) {
14+
return std::array<mph::pair<T, T>, data.size()>{
15+
{{static_cast<T>(data[i].first),
16+
static_cast<T>(data[i].second)}...}};
17+
}(std::make_index_sequence<data.size()>{});
18+
19+
constexpr auto map = mph::hash<input_data>;
20+
21+
return map;
22+
}
23+
24+
template <auto data, typename T>
25+
__attribute__((noinline, flatten)) T do_mph(T k) {
26+
constexpr static auto map = make_mph<data, T>();
27+
return *map(k);
28+
}
29+
30+
template <auto data, typename T> void bench_mph(auto name) {
31+
32+
constexpr auto map = make_mph<data, T>();
33+
34+
// printf("\nmph\n");
35+
do_mph<data, T>(T{});
36+
37+
T k = static_cast<T>(data[0].first);
38+
ankerl::nanobench::Bench().minEpochIterations(2000000).run("chained", [&] {
39+
k = *map(k);
40+
ankerl::nanobench::doNotOptimizeAway(k);
41+
});
42+
43+
auto i = std::size_t{};
44+
ankerl::nanobench::Bench().minEpochIterations(2000000).run(
45+
"independent", [&] {
46+
auto v = *map(static_cast<T>(data[i].first));
47+
i++;
48+
if (i >= data.size()) {
49+
i = 0;
50+
}
51+
ankerl::nanobench::doNotOptimizeAway(v);
52+
});
53+
}

0 commit comments

Comments
 (0)