1*e3feb2ccSEmilio Cota /* SPDX-License-Identifier: GPL-2.0-or-later */
2*e3feb2ccSEmilio Cota #include "qemu/osdep.h"
3*e3feb2ccSEmilio Cota #include "qemu/qtree.h"
4*e3feb2ccSEmilio Cota #include "qemu/timer.h"
5*e3feb2ccSEmilio Cota
6*e3feb2ccSEmilio Cota enum tree_op {
7*e3feb2ccSEmilio Cota OP_LOOKUP,
8*e3feb2ccSEmilio Cota OP_INSERT,
9*e3feb2ccSEmilio Cota OP_REMOVE,
10*e3feb2ccSEmilio Cota OP_REMOVE_ALL,
11*e3feb2ccSEmilio Cota OP_TRAVERSE,
12*e3feb2ccSEmilio Cota };
13*e3feb2ccSEmilio Cota
14*e3feb2ccSEmilio Cota struct benchmark {
15*e3feb2ccSEmilio Cota const char * const name;
16*e3feb2ccSEmilio Cota enum tree_op op;
17*e3feb2ccSEmilio Cota bool fill_on_init;
18*e3feb2ccSEmilio Cota };
19*e3feb2ccSEmilio Cota
20*e3feb2ccSEmilio Cota enum impl_type {
21*e3feb2ccSEmilio Cota IMPL_GTREE,
22*e3feb2ccSEmilio Cota IMPL_QTREE,
23*e3feb2ccSEmilio Cota };
24*e3feb2ccSEmilio Cota
25*e3feb2ccSEmilio Cota struct tree_implementation {
26*e3feb2ccSEmilio Cota const char * const name;
27*e3feb2ccSEmilio Cota enum impl_type type;
28*e3feb2ccSEmilio Cota };
29*e3feb2ccSEmilio Cota
30*e3feb2ccSEmilio Cota static const struct benchmark benchmarks[] = {
31*e3feb2ccSEmilio Cota {
32*e3feb2ccSEmilio Cota .name = "Lookup",
33*e3feb2ccSEmilio Cota .op = OP_LOOKUP,
34*e3feb2ccSEmilio Cota .fill_on_init = true,
35*e3feb2ccSEmilio Cota },
36*e3feb2ccSEmilio Cota {
37*e3feb2ccSEmilio Cota .name = "Insert",
38*e3feb2ccSEmilio Cota .op = OP_INSERT,
39*e3feb2ccSEmilio Cota .fill_on_init = false,
40*e3feb2ccSEmilio Cota },
41*e3feb2ccSEmilio Cota {
42*e3feb2ccSEmilio Cota .name = "Remove",
43*e3feb2ccSEmilio Cota .op = OP_REMOVE,
44*e3feb2ccSEmilio Cota .fill_on_init = true,
45*e3feb2ccSEmilio Cota },
46*e3feb2ccSEmilio Cota {
47*e3feb2ccSEmilio Cota .name = "RemoveAll",
48*e3feb2ccSEmilio Cota .op = OP_REMOVE_ALL,
49*e3feb2ccSEmilio Cota .fill_on_init = true,
50*e3feb2ccSEmilio Cota },
51*e3feb2ccSEmilio Cota {
52*e3feb2ccSEmilio Cota .name = "Traverse",
53*e3feb2ccSEmilio Cota .op = OP_TRAVERSE,
54*e3feb2ccSEmilio Cota .fill_on_init = true,
55*e3feb2ccSEmilio Cota },
56*e3feb2ccSEmilio Cota };
57*e3feb2ccSEmilio Cota
58*e3feb2ccSEmilio Cota static const struct tree_implementation impls[] = {
59*e3feb2ccSEmilio Cota {
60*e3feb2ccSEmilio Cota .name = "GTree",
61*e3feb2ccSEmilio Cota .type = IMPL_GTREE,
62*e3feb2ccSEmilio Cota },
63*e3feb2ccSEmilio Cota {
64*e3feb2ccSEmilio Cota .name = "QTree",
65*e3feb2ccSEmilio Cota .type = IMPL_QTREE,
66*e3feb2ccSEmilio Cota },
67*e3feb2ccSEmilio Cota };
68*e3feb2ccSEmilio Cota
compare_func(const void * ap,const void * bp)69*e3feb2ccSEmilio Cota static int compare_func(const void *ap, const void *bp)
70*e3feb2ccSEmilio Cota {
71*e3feb2ccSEmilio Cota const size_t *a = ap;
72*e3feb2ccSEmilio Cota const size_t *b = bp;
73*e3feb2ccSEmilio Cota
74*e3feb2ccSEmilio Cota return *a - *b;
75*e3feb2ccSEmilio Cota }
76*e3feb2ccSEmilio Cota
init_empty_tree_and_keys(enum impl_type impl,void ** ret_tree,size_t ** ret_keys,size_t n_elems)77*e3feb2ccSEmilio Cota static void init_empty_tree_and_keys(enum impl_type impl,
78*e3feb2ccSEmilio Cota void **ret_tree, size_t **ret_keys,
79*e3feb2ccSEmilio Cota size_t n_elems)
80*e3feb2ccSEmilio Cota {
81*e3feb2ccSEmilio Cota size_t *keys = g_malloc_n(n_elems, sizeof(*keys));
82*e3feb2ccSEmilio Cota for (size_t i = 0; i < n_elems; i++) {
83*e3feb2ccSEmilio Cota keys[i] = i;
84*e3feb2ccSEmilio Cota }
85*e3feb2ccSEmilio Cota
86*e3feb2ccSEmilio Cota void *tree;
87*e3feb2ccSEmilio Cota switch (impl) {
88*e3feb2ccSEmilio Cota case IMPL_GTREE:
89*e3feb2ccSEmilio Cota tree = g_tree_new(compare_func);
90*e3feb2ccSEmilio Cota break;
91*e3feb2ccSEmilio Cota case IMPL_QTREE:
92*e3feb2ccSEmilio Cota tree = q_tree_new(compare_func);
93*e3feb2ccSEmilio Cota break;
94*e3feb2ccSEmilio Cota default:
95*e3feb2ccSEmilio Cota g_assert_not_reached();
96*e3feb2ccSEmilio Cota }
97*e3feb2ccSEmilio Cota
98*e3feb2ccSEmilio Cota *ret_tree = tree;
99*e3feb2ccSEmilio Cota *ret_keys = keys;
100*e3feb2ccSEmilio Cota }
101*e3feb2ccSEmilio Cota
traverse_func(gpointer key,gpointer value,gpointer data)102*e3feb2ccSEmilio Cota static gboolean traverse_func(gpointer key, gpointer value, gpointer data)
103*e3feb2ccSEmilio Cota {
104*e3feb2ccSEmilio Cota return FALSE;
105*e3feb2ccSEmilio Cota }
106*e3feb2ccSEmilio Cota
remove_all(void * tree,enum impl_type impl)107*e3feb2ccSEmilio Cota static inline void remove_all(void *tree, enum impl_type impl)
108*e3feb2ccSEmilio Cota {
109*e3feb2ccSEmilio Cota switch (impl) {
110*e3feb2ccSEmilio Cota case IMPL_GTREE:
111*e3feb2ccSEmilio Cota g_tree_destroy(tree);
112*e3feb2ccSEmilio Cota break;
113*e3feb2ccSEmilio Cota case IMPL_QTREE:
114*e3feb2ccSEmilio Cota q_tree_destroy(tree);
115*e3feb2ccSEmilio Cota break;
116*e3feb2ccSEmilio Cota default:
117*e3feb2ccSEmilio Cota g_assert_not_reached();
118*e3feb2ccSEmilio Cota }
119*e3feb2ccSEmilio Cota }
120*e3feb2ccSEmilio Cota
run_benchmark(const struct benchmark * bench,enum impl_type impl,size_t n_elems)121*e3feb2ccSEmilio Cota static int64_t run_benchmark(const struct benchmark *bench,
122*e3feb2ccSEmilio Cota enum impl_type impl,
123*e3feb2ccSEmilio Cota size_t n_elems)
124*e3feb2ccSEmilio Cota {
125*e3feb2ccSEmilio Cota void *tree;
126*e3feb2ccSEmilio Cota size_t *keys;
127*e3feb2ccSEmilio Cota
128*e3feb2ccSEmilio Cota init_empty_tree_and_keys(impl, &tree, &keys, n_elems);
129*e3feb2ccSEmilio Cota if (bench->fill_on_init) {
130*e3feb2ccSEmilio Cota for (size_t i = 0; i < n_elems; i++) {
131*e3feb2ccSEmilio Cota switch (impl) {
132*e3feb2ccSEmilio Cota case IMPL_GTREE:
133*e3feb2ccSEmilio Cota g_tree_insert(tree, &keys[i], &keys[i]);
134*e3feb2ccSEmilio Cota break;
135*e3feb2ccSEmilio Cota case IMPL_QTREE:
136*e3feb2ccSEmilio Cota q_tree_insert(tree, &keys[i], &keys[i]);
137*e3feb2ccSEmilio Cota break;
138*e3feb2ccSEmilio Cota default:
139*e3feb2ccSEmilio Cota g_assert_not_reached();
140*e3feb2ccSEmilio Cota }
141*e3feb2ccSEmilio Cota }
142*e3feb2ccSEmilio Cota }
143*e3feb2ccSEmilio Cota
144*e3feb2ccSEmilio Cota int64_t start_ns = get_clock();
145*e3feb2ccSEmilio Cota switch (bench->op) {
146*e3feb2ccSEmilio Cota case OP_LOOKUP:
147*e3feb2ccSEmilio Cota for (size_t i = 0; i < n_elems; i++) {
148*e3feb2ccSEmilio Cota void *value;
149*e3feb2ccSEmilio Cota switch (impl) {
150*e3feb2ccSEmilio Cota case IMPL_GTREE:
151*e3feb2ccSEmilio Cota value = g_tree_lookup(tree, &keys[i]);
152*e3feb2ccSEmilio Cota break;
153*e3feb2ccSEmilio Cota case IMPL_QTREE:
154*e3feb2ccSEmilio Cota value = q_tree_lookup(tree, &keys[i]);
155*e3feb2ccSEmilio Cota break;
156*e3feb2ccSEmilio Cota default:
157*e3feb2ccSEmilio Cota g_assert_not_reached();
158*e3feb2ccSEmilio Cota }
159*e3feb2ccSEmilio Cota (void)value;
160*e3feb2ccSEmilio Cota }
161*e3feb2ccSEmilio Cota break;
162*e3feb2ccSEmilio Cota case OP_INSERT:
163*e3feb2ccSEmilio Cota for (size_t i = 0; i < n_elems; i++) {
164*e3feb2ccSEmilio Cota switch (impl) {
165*e3feb2ccSEmilio Cota case IMPL_GTREE:
166*e3feb2ccSEmilio Cota g_tree_insert(tree, &keys[i], &keys[i]);
167*e3feb2ccSEmilio Cota break;
168*e3feb2ccSEmilio Cota case IMPL_QTREE:
169*e3feb2ccSEmilio Cota q_tree_insert(tree, &keys[i], &keys[i]);
170*e3feb2ccSEmilio Cota break;
171*e3feb2ccSEmilio Cota default:
172*e3feb2ccSEmilio Cota g_assert_not_reached();
173*e3feb2ccSEmilio Cota }
174*e3feb2ccSEmilio Cota }
175*e3feb2ccSEmilio Cota break;
176*e3feb2ccSEmilio Cota case OP_REMOVE:
177*e3feb2ccSEmilio Cota for (size_t i = 0; i < n_elems; i++) {
178*e3feb2ccSEmilio Cota switch (impl) {
179*e3feb2ccSEmilio Cota case IMPL_GTREE:
180*e3feb2ccSEmilio Cota g_tree_remove(tree, &keys[i]);
181*e3feb2ccSEmilio Cota break;
182*e3feb2ccSEmilio Cota case IMPL_QTREE:
183*e3feb2ccSEmilio Cota q_tree_remove(tree, &keys[i]);
184*e3feb2ccSEmilio Cota break;
185*e3feb2ccSEmilio Cota default:
186*e3feb2ccSEmilio Cota g_assert_not_reached();
187*e3feb2ccSEmilio Cota }
188*e3feb2ccSEmilio Cota }
189*e3feb2ccSEmilio Cota break;
190*e3feb2ccSEmilio Cota case OP_REMOVE_ALL:
191*e3feb2ccSEmilio Cota remove_all(tree, impl);
192*e3feb2ccSEmilio Cota break;
193*e3feb2ccSEmilio Cota case OP_TRAVERSE:
194*e3feb2ccSEmilio Cota switch (impl) {
195*e3feb2ccSEmilio Cota case IMPL_GTREE:
196*e3feb2ccSEmilio Cota g_tree_foreach(tree, traverse_func, NULL);
197*e3feb2ccSEmilio Cota break;
198*e3feb2ccSEmilio Cota case IMPL_QTREE:
199*e3feb2ccSEmilio Cota q_tree_foreach(tree, traverse_func, NULL);
200*e3feb2ccSEmilio Cota break;
201*e3feb2ccSEmilio Cota default:
202*e3feb2ccSEmilio Cota g_assert_not_reached();
203*e3feb2ccSEmilio Cota }
204*e3feb2ccSEmilio Cota break;
205*e3feb2ccSEmilio Cota default:
206*e3feb2ccSEmilio Cota g_assert_not_reached();
207*e3feb2ccSEmilio Cota }
208*e3feb2ccSEmilio Cota int64_t ns = get_clock() - start_ns;
209*e3feb2ccSEmilio Cota
210*e3feb2ccSEmilio Cota if (bench->op != OP_REMOVE_ALL) {
211*e3feb2ccSEmilio Cota remove_all(tree, impl);
212*e3feb2ccSEmilio Cota }
213*e3feb2ccSEmilio Cota g_free(keys);
214*e3feb2ccSEmilio Cota
215*e3feb2ccSEmilio Cota return ns;
216*e3feb2ccSEmilio Cota }
217*e3feb2ccSEmilio Cota
main(int argc,char * argv[])218*e3feb2ccSEmilio Cota int main(int argc, char *argv[])
219*e3feb2ccSEmilio Cota {
220*e3feb2ccSEmilio Cota size_t sizes[] = {
221*e3feb2ccSEmilio Cota 32,
222*e3feb2ccSEmilio Cota 1024,
223*e3feb2ccSEmilio Cota 1024 * 4,
224*e3feb2ccSEmilio Cota 1024 * 128,
225*e3feb2ccSEmilio Cota 1024 * 1024,
226*e3feb2ccSEmilio Cota };
227*e3feb2ccSEmilio Cota
228*e3feb2ccSEmilio Cota double res[ARRAY_SIZE(benchmarks)][ARRAY_SIZE(impls)][ARRAY_SIZE(sizes)];
229*e3feb2ccSEmilio Cota for (int i = 0; i < ARRAY_SIZE(sizes); i++) {
230*e3feb2ccSEmilio Cota size_t size = sizes[i];
231*e3feb2ccSEmilio Cota for (int j = 0; j < ARRAY_SIZE(impls); j++) {
232*e3feb2ccSEmilio Cota const struct tree_implementation *impl = &impls[j];
233*e3feb2ccSEmilio Cota for (int k = 0; k < ARRAY_SIZE(benchmarks); k++) {
234*e3feb2ccSEmilio Cota const struct benchmark *bench = &benchmarks[k];
235*e3feb2ccSEmilio Cota
236*e3feb2ccSEmilio Cota /* warm-up run */
237*e3feb2ccSEmilio Cota run_benchmark(bench, impl->type, size);
238*e3feb2ccSEmilio Cota
239*e3feb2ccSEmilio Cota int64_t total_ns = 0;
240*e3feb2ccSEmilio Cota int64_t n_runs = 0;
241*e3feb2ccSEmilio Cota while (total_ns < 2e8 || n_runs < 5) {
242*e3feb2ccSEmilio Cota total_ns += run_benchmark(bench, impl->type, size);
243*e3feb2ccSEmilio Cota n_runs++;
244*e3feb2ccSEmilio Cota }
245*e3feb2ccSEmilio Cota double ns_per_run = (double)total_ns / n_runs;
246*e3feb2ccSEmilio Cota
247*e3feb2ccSEmilio Cota /* Throughput, in Mops/s */
248*e3feb2ccSEmilio Cota res[k][j][i] = size / ns_per_run * 1e3;
249*e3feb2ccSEmilio Cota }
250*e3feb2ccSEmilio Cota }
251*e3feb2ccSEmilio Cota }
252*e3feb2ccSEmilio Cota
253*e3feb2ccSEmilio Cota printf("# Results' breakdown: Tree, Op and #Elements. Units: Mops/s\n");
254*e3feb2ccSEmilio Cota printf("%5s %10s ", "Tree", "Op");
255*e3feb2ccSEmilio Cota for (int i = 0; i < ARRAY_SIZE(sizes); i++) {
256*e3feb2ccSEmilio Cota printf("%7zu ", sizes[i]);
257*e3feb2ccSEmilio Cota }
258*e3feb2ccSEmilio Cota printf("\n");
259*e3feb2ccSEmilio Cota char separator[97];
260*e3feb2ccSEmilio Cota for (int i = 0; i < ARRAY_SIZE(separator) - 1; i++) {
261*e3feb2ccSEmilio Cota separator[i] = '-';
262*e3feb2ccSEmilio Cota }
263*e3feb2ccSEmilio Cota separator[ARRAY_SIZE(separator) - 1] = '\0';
264*e3feb2ccSEmilio Cota printf("%s\n", separator);
265*e3feb2ccSEmilio Cota for (int i = 0; i < ARRAY_SIZE(benchmarks); i++) {
266*e3feb2ccSEmilio Cota for (int j = 0; j < ARRAY_SIZE(impls); j++) {
267*e3feb2ccSEmilio Cota printf("%5s %10s ", impls[j].name, benchmarks[i].name);
268*e3feb2ccSEmilio Cota for (int k = 0; k < ARRAY_SIZE(sizes); k++) {
269*e3feb2ccSEmilio Cota printf("%7.2f ", res[i][j][k]);
270*e3feb2ccSEmilio Cota if (j == 0) {
271*e3feb2ccSEmilio Cota printf(" ");
272*e3feb2ccSEmilio Cota } else {
273*e3feb2ccSEmilio Cota if (res[i][0][k] != 0) {
274*e3feb2ccSEmilio Cota double speedup = res[i][j][k] / res[i][0][k];
275*e3feb2ccSEmilio Cota printf("(%4.2fx) ", speedup);
276*e3feb2ccSEmilio Cota } else {
277*e3feb2ccSEmilio Cota printf("( ) ");
278*e3feb2ccSEmilio Cota }
279*e3feb2ccSEmilio Cota }
280*e3feb2ccSEmilio Cota }
281*e3feb2ccSEmilio Cota printf("\n");
282*e3feb2ccSEmilio Cota }
283*e3feb2ccSEmilio Cota }
284*e3feb2ccSEmilio Cota printf("%s\n", separator);
285*e3feb2ccSEmilio Cota return 0;
286*e3feb2ccSEmilio Cota }
287