Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
bench_flat_containers.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 https://github.com/lrleon/Aleph-w
6
7 This file is part of Aleph-w library
8
9 Copyright (c) 2002-2026 Leandro Rabindranath Leon
10*/
11
28#include <algorithm>
29#include <chrono>
30#include <cstdio>
31#include <cstdlib>
32#include <deque>
33#include <map>
34#include <numeric>
35#include <random>
36#include <set>
37#include <string>
38#include <vector>
39
40#include <tpl_dynSetTree.H>
41#include <tpl_dynMapTree.H>
42#include <tpl_flat_map.H>
43#include <tpl_flat_set.H>
44#include <tpl_ring_buffer.H>
45#include <tpl_small_vector.H>
46
47using namespace Aleph;
48
49namespace
50{
51
52volatile long sink = 0; // keeps results observable for the optimizer
53
54// Runs fn `repeats` times and returns the best wall-clock milliseconds.
55template <class Fn>
56double best_ms(Fn fn, int repeats = 3)
57{
58 double best = 1e300;
59 for (int r = 0; r < repeats; ++r)
60 {
61 const auto t0 = std::chrono::steady_clock::now();
62 fn();
63 const auto t1 = std::chrono::steady_clock::now();
64 const double ms =
65 std::chrono::duration<double, std::milli>(t1 - t0).count();
66 best = std::min(best, ms);
67 }
68 return best;
69}
70
71void row(const char *container, const char *op, double ms)
72{
73 std::printf(" %-12s %-24s %10.2f ms\n", container, op, ms);
74}
75
76std::vector<long> shuffled_keys(size_t n, unsigned seed)
77{
78 std::vector<long> keys(n);
79 std::iota(keys.begin(), keys.end(), 0L);
80 std::shuffle(keys.begin(), keys.end(), std::mt19937(seed));
81 return keys;
82}
83
84void bench_sets(size_t lookup_n, size_t insert_n)
85{
86 std::printf("\n== Ordered sets (lookup/iteration size %zu, incremental "
87 "insert size %zu) ==\n", lookup_n, insert_n);
88
89 const auto keys = shuffled_keys(lookup_n, 1u);
90 const auto ins_keys = shuffled_keys(insert_n, 2u);
91
92 // Bulk build (the flat container's natural loading mode).
93 row("FlatSet", "bulk build",
94 best_ms([&] { FlatSet<long> s(keys.begin(), keys.end());
95 sink += static_cast<long>(s.size()); }));
96 row("DynSetTree", "bulk build",
98 for (const long k : keys) s.insert(k);
99 sink += static_cast<long>(s.size()); }));
100 row("std::set", "bulk build",
101 best_ms([&] { std::set<long> s(keys.begin(), keys.end());
102 sink += static_cast<long>(s.size()); }));
103
104 // Incremental random insertion (the flat container's worst case).
105 row("FlatSet", "random insert",
106 best_ms([&] { FlatSet<long> s;
107 for (const long k : ins_keys) s.insert(k);
108 sink += static_cast<long>(s.size()); }));
109 row("DynSetTree", "random insert",
111 for (const long k : ins_keys) s.insert(k);
112 sink += static_cast<long>(s.size()); }));
113 row("std::set", "random insert",
114 best_ms([&] { std::set<long> s;
115 for (const long k : ins_keys) s.insert(k);
116 sink += static_cast<long>(s.size()); }));
117
118 // Lookup (half hits, half misses) on prebuilt containers.
119 FlatSet<long> fs(keys.begin(), keys.end());
121 for (const long k : keys)
122 ts.insert(k);
123 std::set<long> ss(keys.begin(), keys.end());
124 const long span = 2 * static_cast<long>(lookup_n);
125
126 row("FlatSet", "lookup 50% hits",
127 best_ms([&] { long hits = 0;
128 for (long k = 0; k < span; ++k) hits += fs.contains(k);
129 sink += hits; }));
130 row("DynSetTree", "lookup 50% hits",
131 best_ms([&] { long hits = 0;
132 for (long k = 0; k < span; ++k) hits += ts.contains(k);
133 sink += hits; }));
134 row("std::set", "lookup 50% hits",
135 best_ms([&] { long hits = 0;
136 for (long k = 0; k < span; ++k) hits += ss.count(k);
137 sink += hits; }));
138
139 // Full iteration.
140 row("FlatSet", "iterate + sum",
141 best_ms([&] { long acc = 0;
142 for (const long k : fs) acc += k;
143 sink += acc; }));
144 row("DynSetTree", "iterate + sum",
145 best_ms([&] { long acc = 0;
146 ts.for_each([&acc] (const long &k) { acc += k; });
147 sink += acc; }));
148 row("std::set", "iterate + sum",
149 best_ms([&] { long acc = 0;
150 for (const long k : ss) acc += k;
151 sink += acc; }));
152}
153
154void bench_maps(size_t lookup_n, size_t insert_n)
155{
156 std::printf("\n== Ordered maps (lookup size %zu, incremental insert size "
157 "%zu) ==\n", lookup_n, insert_n);
158
159 const auto keys = shuffled_keys(lookup_n, 3u);
160 const auto ins_keys = shuffled_keys(insert_n, 4u);
161
162 row("FlatMap", "random insert",
164 for (const long k : ins_keys) m.insert(k, 2 * k);
165 sink += static_cast<long>(m.size()); }));
166 row("DynMapTree", "random insert",
168 for (const long k : ins_keys) m.insert(k, 2 * k);
169 sink += static_cast<long>(m.size()); }));
170 row("std::map", "random insert",
171 best_ms([&] { std::map<long, long> m;
172 for (const long k : ins_keys) m.insert({k, 2 * k});
173 sink += static_cast<long>(m.size()); }));
174
176 for (const long k : keys)
177 fm.insert(k, 2 * k);
179 for (const long k : keys)
180 tm.insert(k, 2 * k);
181 std::map<long, long> sm;
182 for (const long k : keys)
183 sm.insert({k, 2 * k});
184 const long span = 2 * static_cast<long>(lookup_n);
185
186 row("FlatMap", "lookup 50% hits",
187 best_ms([&] { long hits = 0;
188 for (long k = 0; k < span; ++k) hits += fm.contains(k);
189 sink += hits; }));
190 row("DynMapTree", "lookup 50% hits",
191 best_ms([&] { long hits = 0;
192 for (long k = 0; k < span; ++k) hits += tm.contains(k);
193 sink += hits; }));
194 row("std::map", "lookup 50% hits",
195 best_ms([&] { long hits = 0;
196 for (long k = 0; k < span; ++k) hits += sm.count(k);
197 sink += hits; }));
198
199 row("FlatMap", "iterate + sum values",
200 best_ms([&] { long acc = 0;
201 for (auto [k, v] : fm) acc += v;
202 sink += acc; }));
203 row("DynMapTree", "iterate + sum values",
204 best_ms([&] { long acc = 0;
205 tm.for_each([&acc] (const std::pair<long, long> &p)
206 { acc += p.second; });
207 sink += acc; }));
208 row("std::map", "iterate + sum values",
209 best_ms([&] { long acc = 0;
210 for (const auto &[k, v] : sm) acc += v;
211 sink += acc; }));
212}
213
214void bench_small_vectors(size_t rounds)
215{
216 std::printf("\n== Small sequences (%zu rounds of 8 appends each) ==\n",
217 rounds);
218
219 row("SmallVector", "build 8-elem seq",
220 best_ms([&] { long acc = 0;
221 for (size_t r = 0; r < rounds; ++r)
222 {
223 SmallVector<long, 8> v; // inline: no allocation
224 for (long i = 0; i < 8; ++i)
225 v.append(i + static_cast<long>(r));
226 acc += v.get_last();
227 }
228 sink += acc; }));
229 row("std::vector", "build 8-elem seq",
230 best_ms([&] { long acc = 0;
231 for (size_t r = 0; r < rounds; ++r)
232 {
233 std::vector<long> v; // always allocates
234 for (long i = 0; i < 8; ++i)
235 v.push_back(i + static_cast<long>(r));
236 acc += v.back();
237 }
238 sink += acc; }));
239}
240
241void bench_ring_buffers(size_t stream_n)
242{
243 std::printf("\n== Sliding window over a stream of %zu samples "
244 "(window 1024) ==\n", stream_n);
245 constexpr size_t window = 1024;
246
247 row("RingBuffer", "put_overwrite stream",
248 best_ms([&] { RingBuffer<long> rb(window);
249 for (size_t i = 0; i < stream_n; ++i)
250 rb.put_overwrite(static_cast<long>(i));
251 sink += rb.get_last(); }));
252 row("std::deque", "bounded push/pop",
253 best_ms([&] { std::deque<long> dq;
254 for (size_t i = 0; i < stream_n; ++i)
255 {
256 dq.push_back(static_cast<long>(i));
257 if (dq.size() > window)
258 dq.pop_front();
259 }
260 sink += dq.back(); }));
261}
262
263} // namespace
264
265int main(int argc, char *argv[])
266{
267 const size_t lookup_n = argc > 1 ? std::strtoul(argv[1], nullptr, 10)
268 : 200000;
269 const size_t insert_n = argc > 2 ? std::strtoul(argv[2], nullptr, 10)
270 : 20000;
271
272 std::printf("Flat containers benchmark (best of 3 runs)\n");
275 bench_small_vectors(200000);
276 bench_ring_buffers(5000000);
277 std::printf("\n(sink=%ld)\n", static_cast<long>(sink));
278 return 0;
279}
int main()
size_t row
Definition ca-c-api.h:115
Generic key-value map implemented on top of a binary search tree.
Dynamic set backed by balanced binary search trees with automatic memory management.
const size_t & size() const
Returns the cardinality of the set.
Ordered map stored as two parallel sorted contiguous arrays.
Ordered set stored as a sorted contiguous array.
size_t size() const noexcept
Return the number of stored keys. O(1).
Fixed-capacity circular FIFO buffer over contiguous storage.
Contiguous dynamic array with N elements of inline storage.
T & get_last()
Last element (checked).
T & append(const T &item)
Append a copy of item.
constexpr size_t size() const noexcept
Returns the number of entries in the table.
Definition hashDry.H:619
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
int keys[]
ValueArg< size_t > seed
Definition testHash.C:53
static int * k
gsl_rng * r
Dynamic key-value map based on balanced binary search trees.
Dynamic set implementations based on balanced binary search trees.
Sorted-array map (Aleph::FlatMap), a cache-friendly ordered map.
Sorted-array set (Aleph::FlatSet), a cache-friendly ordered set.
Bounded circular buffer (Aleph::RingBuffer) for FIFO streaming.
Dynamic array with inline storage (Aleph::SmallVector).