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 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 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 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 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 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 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