xref: /openbmc/qemu/tests/bench/qht-bench.c (revision 5df022cf)
13b472e71SThomas Huth /*
23b472e71SThomas Huth  * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
33b472e71SThomas Huth  *
43b472e71SThomas Huth  * License: GNU GPL, version 2 or later.
53b472e71SThomas Huth  *   See the COPYING file in the top-level directory.
63b472e71SThomas Huth  */
73b472e71SThomas Huth #include "qemu/osdep.h"
83b472e71SThomas Huth #include "qemu/processor.h"
93b472e71SThomas Huth #include "qemu/atomic.h"
103b472e71SThomas Huth #include "qemu/qht.h"
113b472e71SThomas Huth #include "qemu/rcu.h"
123b472e71SThomas Huth #include "qemu/xxhash.h"
13*5df022cfSPeter Maydell #include "qemu/memalign.h"
143b472e71SThomas Huth 
153b472e71SThomas Huth struct thread_stats {
163b472e71SThomas Huth     size_t rd;
173b472e71SThomas Huth     size_t not_rd;
183b472e71SThomas Huth     size_t in;
193b472e71SThomas Huth     size_t not_in;
203b472e71SThomas Huth     size_t rm;
213b472e71SThomas Huth     size_t not_rm;
223b472e71SThomas Huth     size_t rz;
233b472e71SThomas Huth     size_t not_rz;
243b472e71SThomas Huth };
253b472e71SThomas Huth 
263b472e71SThomas Huth struct thread_info {
273b472e71SThomas Huth     void (*func)(struct thread_info *);
283b472e71SThomas Huth     struct thread_stats stats;
293b472e71SThomas Huth     /*
303b472e71SThomas Huth      * Seed is in the range [1..UINT64_MAX], because the RNG requires
313b472e71SThomas Huth      * a non-zero seed.  To use, subtract 1 and compare against the
323b472e71SThomas Huth      * threshold with </>=.  This lets threshold = 0 never match (0% hit),
333b472e71SThomas Huth      * and threshold = UINT64_MAX always match (100% hit).
343b472e71SThomas Huth      */
353b472e71SThomas Huth     uint64_t seed;
363b472e71SThomas Huth     bool write_op; /* writes alternate between insertions and removals */
373b472e71SThomas Huth     bool resize_down;
383b472e71SThomas Huth } QEMU_ALIGNED(64); /* avoid false sharing among threads */
393b472e71SThomas Huth 
403b472e71SThomas Huth static struct qht ht;
413b472e71SThomas Huth static QemuThread *rw_threads;
423b472e71SThomas Huth 
433b472e71SThomas Huth #define DEFAULT_RANGE (4096)
443b472e71SThomas Huth #define DEFAULT_QHT_N_ELEMS DEFAULT_RANGE
453b472e71SThomas Huth 
463b472e71SThomas Huth static unsigned int duration = 1;
473b472e71SThomas Huth static unsigned int n_rw_threads = 1;
483b472e71SThomas Huth static unsigned long lookup_range = DEFAULT_RANGE;
493b472e71SThomas Huth static unsigned long update_range = DEFAULT_RANGE;
503b472e71SThomas Huth static size_t init_range = DEFAULT_RANGE;
513b472e71SThomas Huth static size_t init_size = DEFAULT_RANGE;
523b472e71SThomas Huth static size_t n_ready_threads;
533b472e71SThomas Huth static long populate_offset;
543b472e71SThomas Huth static long *keys;
553b472e71SThomas Huth 
563b472e71SThomas Huth static size_t resize_min;
573b472e71SThomas Huth static size_t resize_max;
583b472e71SThomas Huth static struct thread_info *rz_info;
593b472e71SThomas Huth static unsigned long resize_delay = 1000;
603b472e71SThomas Huth static double resize_rate; /* 0.0 to 1.0 */
613b472e71SThomas Huth static unsigned int n_rz_threads = 1;
623b472e71SThomas Huth static QemuThread *rz_threads;
633b472e71SThomas Huth static bool precompute_hash;
643b472e71SThomas Huth 
653b472e71SThomas Huth static double update_rate; /* 0.0 to 1.0 */
663b472e71SThomas Huth static uint64_t update_threshold;
673b472e71SThomas Huth static uint64_t resize_threshold;
683b472e71SThomas Huth 
693b472e71SThomas Huth static size_t qht_n_elems = DEFAULT_QHT_N_ELEMS;
703b472e71SThomas Huth static int qht_mode;
713b472e71SThomas Huth 
723b472e71SThomas Huth static bool test_start;
733b472e71SThomas Huth static bool test_stop;
743b472e71SThomas Huth 
753b472e71SThomas Huth static struct thread_info *rw_info;
763b472e71SThomas Huth 
773b472e71SThomas Huth static const char commands_string[] =
783b472e71SThomas Huth     " -d = duration, in seconds\n"
793b472e71SThomas Huth     " -n = number of threads\n"
803b472e71SThomas Huth     "\n"
813b472e71SThomas Huth     " -o = offset at which keys start\n"
823b472e71SThomas Huth     " -p = precompute hashes\n"
833b472e71SThomas Huth     "\n"
843b472e71SThomas Huth     " -g = set -s,-k,-K,-l,-r to the same value\n"
853b472e71SThomas Huth     " -s = initial size hint\n"
863b472e71SThomas Huth     " -k = initial number of keys\n"
873b472e71SThomas Huth     " -K = initial range of keys (will be rounded up to pow2)\n"
883b472e71SThomas Huth     " -l = lookup range of keys (will be rounded up to pow2)\n"
893b472e71SThomas Huth     " -r = update range of keys (will be rounded up to pow2)\n"
903b472e71SThomas Huth     "\n"
913b472e71SThomas Huth     " -u = update rate (0.0 to 100.0), 50/50 split of insertions/removals\n"
923b472e71SThomas Huth     "\n"
933b472e71SThomas Huth     " -R = enable auto-resize\n"
943b472e71SThomas Huth     " -S = resize rate (0.0 to 100.0)\n"
953b472e71SThomas Huth     " -D = delay (in us) between potential resizes\n"
963b472e71SThomas Huth     " -N = number of resize threads";
973b472e71SThomas Huth 
usage_complete(int argc,char * argv[])983b472e71SThomas Huth static void usage_complete(int argc, char *argv[])
993b472e71SThomas Huth {
1003b472e71SThomas Huth     fprintf(stderr, "Usage: %s [options]\n", argv[0]);
1013b472e71SThomas Huth     fprintf(stderr, "options:\n%s\n", commands_string);
1023b472e71SThomas Huth     exit(-1);
1033b472e71SThomas Huth }
1043b472e71SThomas Huth 
is_equal(const void * ap,const void * bp)1053b472e71SThomas Huth static bool is_equal(const void *ap, const void *bp)
1063b472e71SThomas Huth {
1073b472e71SThomas Huth     const long *a = ap;
1083b472e71SThomas Huth     const long *b = bp;
1093b472e71SThomas Huth 
1103b472e71SThomas Huth     return *a == *b;
1113b472e71SThomas Huth }
1123b472e71SThomas Huth 
h(unsigned long v)1133b472e71SThomas Huth static uint32_t h(unsigned long v)
1143b472e71SThomas Huth {
1153b472e71SThomas Huth     return qemu_xxhash2(v);
1163b472e71SThomas Huth }
1173b472e71SThomas Huth 
hval(unsigned long v)1183b472e71SThomas Huth static uint32_t hval(unsigned long v)
1193b472e71SThomas Huth {
1203b472e71SThomas Huth     return v;
1213b472e71SThomas Huth }
1223b472e71SThomas Huth 
1233b472e71SThomas Huth static uint32_t (*hfunc)(unsigned long v) = h;
1243b472e71SThomas Huth 
1253b472e71SThomas Huth /*
1263b472e71SThomas Huth  * From: https://en.wikipedia.org/wiki/Xorshift
1273b472e71SThomas Huth  * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
1283b472e71SThomas Huth  * guaranteed to be >= INT_MAX).
1293b472e71SThomas Huth  */
xorshift64star(uint64_t x)1303b472e71SThomas Huth static uint64_t xorshift64star(uint64_t x)
1313b472e71SThomas Huth {
1323b472e71SThomas Huth     x ^= x >> 12; /* a */
1333b472e71SThomas Huth     x ^= x << 25; /* b */
1343b472e71SThomas Huth     x ^= x >> 27; /* c */
1353b472e71SThomas Huth     return x * UINT64_C(2685821657736338717);
1363b472e71SThomas Huth }
1373b472e71SThomas Huth 
do_rz(struct thread_info * info)1383b472e71SThomas Huth static void do_rz(struct thread_info *info)
1393b472e71SThomas Huth {
1403b472e71SThomas Huth     struct thread_stats *stats = &info->stats;
1413b472e71SThomas Huth     uint64_t r = info->seed - 1;
1423b472e71SThomas Huth 
1433b472e71SThomas Huth     if (r < resize_threshold) {
1443b472e71SThomas Huth         size_t size = info->resize_down ? resize_min : resize_max;
1453b472e71SThomas Huth         bool resized;
1463b472e71SThomas Huth 
1473b472e71SThomas Huth         resized = qht_resize(&ht, size);
1483b472e71SThomas Huth         info->resize_down = !info->resize_down;
1493b472e71SThomas Huth 
1503b472e71SThomas Huth         if (resized) {
1513b472e71SThomas Huth             stats->rz++;
1523b472e71SThomas Huth         } else {
1533b472e71SThomas Huth             stats->not_rz++;
1543b472e71SThomas Huth         }
1553b472e71SThomas Huth     }
1563b472e71SThomas Huth     g_usleep(resize_delay);
1573b472e71SThomas Huth }
1583b472e71SThomas Huth 
do_rw(struct thread_info * info)1593b472e71SThomas Huth static void do_rw(struct thread_info *info)
1603b472e71SThomas Huth {
1613b472e71SThomas Huth     struct thread_stats *stats = &info->stats;
1623b472e71SThomas Huth     uint64_t r = info->seed - 1;
1633b472e71SThomas Huth     uint32_t hash;
1643b472e71SThomas Huth     long *p;
1653b472e71SThomas Huth 
1663b472e71SThomas Huth     if (r >= update_threshold) {
1673b472e71SThomas Huth         bool read;
1683b472e71SThomas Huth 
1693b472e71SThomas Huth         p = &keys[r & (lookup_range - 1)];
1703b472e71SThomas Huth         hash = hfunc(*p);
1713b472e71SThomas Huth         read = qht_lookup(&ht, p, hash);
1723b472e71SThomas Huth         if (read) {
1733b472e71SThomas Huth             stats->rd++;
1743b472e71SThomas Huth         } else {
1753b472e71SThomas Huth             stats->not_rd++;
1763b472e71SThomas Huth         }
1773b472e71SThomas Huth     } else {
1783b472e71SThomas Huth         p = &keys[r & (update_range - 1)];
1793b472e71SThomas Huth         hash = hfunc(*p);
1803b472e71SThomas Huth         if (info->write_op) {
1813b472e71SThomas Huth             bool written = false;
1823b472e71SThomas Huth 
1833b472e71SThomas Huth             if (qht_lookup(&ht, p, hash) == NULL) {
1843b472e71SThomas Huth                 written = qht_insert(&ht, p, hash, NULL);
1853b472e71SThomas Huth             }
1863b472e71SThomas Huth             if (written) {
1873b472e71SThomas Huth                 stats->in++;
1883b472e71SThomas Huth             } else {
1893b472e71SThomas Huth                 stats->not_in++;
1903b472e71SThomas Huth             }
1913b472e71SThomas Huth         } else {
1923b472e71SThomas Huth             bool removed = false;
1933b472e71SThomas Huth 
1943b472e71SThomas Huth             if (qht_lookup(&ht, p, hash)) {
1953b472e71SThomas Huth                 removed = qht_remove(&ht, p, hash);
1963b472e71SThomas Huth             }
1973b472e71SThomas Huth             if (removed) {
1983b472e71SThomas Huth                 stats->rm++;
1993b472e71SThomas Huth             } else {
2003b472e71SThomas Huth                 stats->not_rm++;
2013b472e71SThomas Huth             }
2023b472e71SThomas Huth         }
2033b472e71SThomas Huth         info->write_op = !info->write_op;
2043b472e71SThomas Huth     }
2053b472e71SThomas Huth }
2063b472e71SThomas Huth 
thread_func(void * p)2073b472e71SThomas Huth static void *thread_func(void *p)
2083b472e71SThomas Huth {
2093b472e71SThomas Huth     struct thread_info *info = p;
2103b472e71SThomas Huth 
2113b472e71SThomas Huth     rcu_register_thread();
2123b472e71SThomas Huth 
2133b472e71SThomas Huth     qatomic_inc(&n_ready_threads);
2143b472e71SThomas Huth     while (!qatomic_read(&test_start)) {
2153b472e71SThomas Huth         cpu_relax();
2163b472e71SThomas Huth     }
2173b472e71SThomas Huth 
2183b472e71SThomas Huth     rcu_read_lock();
2193b472e71SThomas Huth     while (!qatomic_read(&test_stop)) {
2203b472e71SThomas Huth         info->seed = xorshift64star(info->seed);
2213b472e71SThomas Huth         info->func(info);
2223b472e71SThomas Huth     }
2233b472e71SThomas Huth     rcu_read_unlock();
2243b472e71SThomas Huth 
2253b472e71SThomas Huth     rcu_unregister_thread();
2263b472e71SThomas Huth     return NULL;
2273b472e71SThomas Huth }
2283b472e71SThomas Huth 
2293b472e71SThomas Huth /* sets everything except info->func */
prepare_thread_info(struct thread_info * info,int i)2303b472e71SThomas Huth static void prepare_thread_info(struct thread_info *info, int i)
2313b472e71SThomas Huth {
2323b472e71SThomas Huth     /* seed for the RNG; each thread should have a different one */
2333b472e71SThomas Huth     info->seed = (i + 1) ^ time(NULL);
2343b472e71SThomas Huth     /* the first update will be a write */
2353b472e71SThomas Huth     info->write_op = true;
2363b472e71SThomas Huth     /* the first resize will be down */
2373b472e71SThomas Huth     info->resize_down = true;
2383b472e71SThomas Huth 
2393b472e71SThomas Huth     memset(&info->stats, 0, sizeof(info->stats));
2403b472e71SThomas Huth }
2413b472e71SThomas Huth 
2423b472e71SThomas Huth static void
th_create_n(QemuThread ** threads,struct thread_info ** infos,const char * name,void (* func)(struct thread_info *),int offset,int n)2433b472e71SThomas Huth th_create_n(QemuThread **threads, struct thread_info **infos, const char *name,
2443b472e71SThomas Huth             void (*func)(struct thread_info *), int offset, int n)
2453b472e71SThomas Huth {
2463b472e71SThomas Huth     struct thread_info *info;
2473b472e71SThomas Huth     QemuThread *th;
2483b472e71SThomas Huth     int i;
2493b472e71SThomas Huth 
2503b472e71SThomas Huth     th = g_malloc(sizeof(*th) * n);
2513b472e71SThomas Huth     *threads = th;
2523b472e71SThomas Huth 
2533b472e71SThomas Huth     info = qemu_memalign(64, sizeof(*info) * n);
2543b472e71SThomas Huth     *infos = info;
2553b472e71SThomas Huth 
2563b472e71SThomas Huth     for (i = 0; i < n; i++) {
2573b472e71SThomas Huth         prepare_thread_info(&info[i], offset + i);
2583b472e71SThomas Huth         info[i].func = func;
2593b472e71SThomas Huth         qemu_thread_create(&th[i], name, thread_func, &info[i],
2603b472e71SThomas Huth                            QEMU_THREAD_JOINABLE);
2613b472e71SThomas Huth     }
2623b472e71SThomas Huth }
2633b472e71SThomas Huth 
create_threads(void)2643b472e71SThomas Huth static void create_threads(void)
2653b472e71SThomas Huth {
2663b472e71SThomas Huth     th_create_n(&rw_threads, &rw_info, "rw", do_rw, 0, n_rw_threads);
2673b472e71SThomas Huth     th_create_n(&rz_threads, &rz_info, "rz", do_rz, n_rw_threads, n_rz_threads);
2683b472e71SThomas Huth }
2693b472e71SThomas Huth 
pr_params(void)2703b472e71SThomas Huth static void pr_params(void)
2713b472e71SThomas Huth {
2723b472e71SThomas Huth     printf("Parameters:\n");
2733b472e71SThomas Huth     printf(" duration:          %d s\n", duration);
2743b472e71SThomas Huth     printf(" # of threads:      %u\n", n_rw_threads);
2753b472e71SThomas Huth     printf(" initial # of keys: %zu\n", init_size);
2763b472e71SThomas Huth     printf(" initial size hint: %zu\n", qht_n_elems);
2773b472e71SThomas Huth     printf(" auto-resize:       %s\n",
2783b472e71SThomas Huth            qht_mode & QHT_MODE_AUTO_RESIZE ? "on" : "off");
2793b472e71SThomas Huth     if (resize_rate) {
2803b472e71SThomas Huth         printf(" resize_rate:       %f%%\n", resize_rate * 100.0);
2813b472e71SThomas Huth         printf(" resize range:      %zu-%zu\n", resize_min, resize_max);
2823b472e71SThomas Huth         printf(" # resize threads   %u\n", n_rz_threads);
2833b472e71SThomas Huth     }
2843b472e71SThomas Huth     printf(" update rate:       %f%%\n", update_rate * 100.0);
2853b472e71SThomas Huth     printf(" offset:            %ld\n", populate_offset);
2863b472e71SThomas Huth     printf(" initial key range: %zu\n", init_range);
2873b472e71SThomas Huth     printf(" lookup range:      %lu\n", lookup_range);
2883b472e71SThomas Huth     printf(" update range:      %lu\n", update_range);
2893b472e71SThomas Huth }
2903b472e71SThomas Huth 
do_threshold(double rate,uint64_t * threshold)2913b472e71SThomas Huth static void do_threshold(double rate, uint64_t *threshold)
2923b472e71SThomas Huth {
2933b472e71SThomas Huth     /*
2943b472e71SThomas Huth      * For 0 <= rate <= 1, scale to fit in a uint64_t.
2953b472e71SThomas Huth      *
2963b472e71SThomas Huth      * Scale by 2**64, with a special case for 1.0.
2973b472e71SThomas Huth      * The remainder of the possible values are scattered between 0
2983b472e71SThomas Huth      * and 0xfffffffffffff800 (nextafter(0x1p64, 0)).
2993b472e71SThomas Huth      *
3003b472e71SThomas Huth      * Note that we cannot simply scale by UINT64_MAX, because that
3013b472e71SThomas Huth      * value is not representable as an IEEE double value.
3023b472e71SThomas Huth      *
3033b472e71SThomas Huth      * If we scale by the next largest value, nextafter(0x1p64, 0),
3043b472e71SThomas Huth      * then the remainder of the possible values are scattered between
3053b472e71SThomas Huth      * 0 and 0xfffffffffffff000.  Which leaves us with a gap between
3063b472e71SThomas Huth      * the final two inputs that is twice as large as any other.
3073b472e71SThomas Huth      */
3083b472e71SThomas Huth     if (rate == 1.0) {
3093b472e71SThomas Huth         *threshold = UINT64_MAX;
3103b472e71SThomas Huth     } else {
3113b472e71SThomas Huth         *threshold = rate * 0x1p64;
3123b472e71SThomas Huth     }
3133b472e71SThomas Huth }
3143b472e71SThomas Huth 
htable_init(void)3153b472e71SThomas Huth static void htable_init(void)
3163b472e71SThomas Huth {
3173b472e71SThomas Huth     unsigned long n = MAX(init_range, update_range);
3183b472e71SThomas Huth     uint64_t r = time(NULL);
3193b472e71SThomas Huth     size_t retries = 0;
3203b472e71SThomas Huth     size_t i;
3213b472e71SThomas Huth 
3223b472e71SThomas Huth     /* avoid allocating memory later by allocating all the keys now */
3233b472e71SThomas Huth     keys = g_malloc(sizeof(*keys) * n);
3243b472e71SThomas Huth     for (i = 0; i < n; i++) {
3253b472e71SThomas Huth         long val = populate_offset + i;
3263b472e71SThomas Huth 
3273b472e71SThomas Huth         keys[i] = precompute_hash ? h(val) : hval(val);
3283b472e71SThomas Huth     }
3293b472e71SThomas Huth 
3303b472e71SThomas Huth     /* some sanity checks */
3313b472e71SThomas Huth     g_assert_cmpuint(lookup_range, <=, n);
3323b472e71SThomas Huth 
3333b472e71SThomas Huth     /* compute thresholds */
3343b472e71SThomas Huth     do_threshold(update_rate, &update_threshold);
3353b472e71SThomas Huth     do_threshold(resize_rate, &resize_threshold);
3363b472e71SThomas Huth 
3373b472e71SThomas Huth     if (resize_rate) {
3383b472e71SThomas Huth         resize_min = n / 2;
3393b472e71SThomas Huth         resize_max = n;
3403b472e71SThomas Huth         assert(resize_min < resize_max);
3413b472e71SThomas Huth     } else {
3423b472e71SThomas Huth         n_rz_threads = 0;
3433b472e71SThomas Huth     }
3443b472e71SThomas Huth 
3453b472e71SThomas Huth     /* initialize the hash table */
3463b472e71SThomas Huth     qht_init(&ht, is_equal, qht_n_elems, qht_mode);
3473b472e71SThomas Huth     assert(init_size <= init_range);
3483b472e71SThomas Huth 
3493b472e71SThomas Huth     pr_params();
3503b472e71SThomas Huth 
3513b472e71SThomas Huth     fprintf(stderr, "Initialization: populating %zu items...", init_size);
3523b472e71SThomas Huth     for (i = 0; i < init_size; i++) {
3533b472e71SThomas Huth         for (;;) {
3543b472e71SThomas Huth             uint32_t hash;
3553b472e71SThomas Huth             long *p;
3563b472e71SThomas Huth 
3573b472e71SThomas Huth             r = xorshift64star(r);
3583b472e71SThomas Huth             p = &keys[r & (init_range - 1)];
3593b472e71SThomas Huth             hash = hfunc(*p);
3603b472e71SThomas Huth             if (qht_insert(&ht, p, hash, NULL)) {
3613b472e71SThomas Huth                 break;
3623b472e71SThomas Huth             }
3633b472e71SThomas Huth             retries++;
3643b472e71SThomas Huth         }
3653b472e71SThomas Huth     }
3663b472e71SThomas Huth     fprintf(stderr, " populated after %zu retries\n", retries);
3673b472e71SThomas Huth }
3683b472e71SThomas Huth 
add_stats(struct thread_stats * s,struct thread_info * info,int n)3693b472e71SThomas Huth static void add_stats(struct thread_stats *s, struct thread_info *info, int n)
3703b472e71SThomas Huth {
3713b472e71SThomas Huth     int i;
3723b472e71SThomas Huth 
3733b472e71SThomas Huth     for (i = 0; i < n; i++) {
3743b472e71SThomas Huth         struct thread_stats *stats = &info[i].stats;
3753b472e71SThomas Huth 
3763b472e71SThomas Huth         s->rd += stats->rd;
3773b472e71SThomas Huth         s->not_rd += stats->not_rd;
3783b472e71SThomas Huth 
3793b472e71SThomas Huth         s->in += stats->in;
3803b472e71SThomas Huth         s->not_in += stats->not_in;
3813b472e71SThomas Huth 
3823b472e71SThomas Huth         s->rm += stats->rm;
3833b472e71SThomas Huth         s->not_rm += stats->not_rm;
3843b472e71SThomas Huth 
3853b472e71SThomas Huth         s->rz += stats->rz;
3863b472e71SThomas Huth         s->not_rz += stats->not_rz;
3873b472e71SThomas Huth     }
3883b472e71SThomas Huth }
3893b472e71SThomas Huth 
pr_stats(void)3903b472e71SThomas Huth static void pr_stats(void)
3913b472e71SThomas Huth {
3923b472e71SThomas Huth     struct thread_stats s = {};
3933b472e71SThomas Huth     double tx;
3943b472e71SThomas Huth 
3953b472e71SThomas Huth     add_stats(&s, rw_info, n_rw_threads);
3963b472e71SThomas Huth     add_stats(&s, rz_info, n_rz_threads);
3973b472e71SThomas Huth 
3983b472e71SThomas Huth     printf("Results:\n");
3993b472e71SThomas Huth 
4003b472e71SThomas Huth     if (resize_rate) {
4013b472e71SThomas Huth         printf(" Resizes:           %zu (%.2f%% of %zu)\n",
4023b472e71SThomas Huth                s.rz, (double)s.rz / (s.rz + s.not_rz) * 100, s.rz + s.not_rz);
4033b472e71SThomas Huth     }
4043b472e71SThomas Huth 
4053b472e71SThomas Huth     printf(" Read:              %.2f M (%.2f%% of %.2fM)\n",
4063b472e71SThomas Huth            (double)s.rd / 1e6,
4073b472e71SThomas Huth            (double)s.rd / (s.rd + s.not_rd) * 100,
4083b472e71SThomas Huth            (double)(s.rd + s.not_rd) / 1e6);
4093b472e71SThomas Huth     printf(" Inserted:          %.2f M (%.2f%% of %.2fM)\n",
4103b472e71SThomas Huth            (double)s.in / 1e6,
4113b472e71SThomas Huth            (double)s.in / (s.in + s.not_in) * 100,
4123b472e71SThomas Huth            (double)(s.in + s.not_in) / 1e6);
4133b472e71SThomas Huth     printf(" Removed:           %.2f M (%.2f%% of %.2fM)\n",
4143b472e71SThomas Huth            (double)s.rm / 1e6,
4153b472e71SThomas Huth            (double)s.rm / (s.rm + s.not_rm) * 100,
4163b472e71SThomas Huth            (double)(s.rm + s.not_rm) / 1e6);
4173b472e71SThomas Huth 
4183b472e71SThomas Huth     tx = (s.rd + s.not_rd + s.in + s.not_in + s.rm + s.not_rm) / 1e6 / duration;
4193b472e71SThomas Huth     printf(" Throughput:        %.2f MT/s\n", tx);
4203b472e71SThomas Huth     printf(" Throughput/thread: %.2f MT/s/thread\n", tx / n_rw_threads);
4213b472e71SThomas Huth }
4223b472e71SThomas Huth 
run_test(void)4233b472e71SThomas Huth static void run_test(void)
4243b472e71SThomas Huth {
4253b472e71SThomas Huth     int i;
4263b472e71SThomas Huth 
4273b472e71SThomas Huth     while (qatomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) {
4283b472e71SThomas Huth         cpu_relax();
4293b472e71SThomas Huth     }
4303b472e71SThomas Huth 
4313b472e71SThomas Huth     qatomic_set(&test_start, true);
4323b472e71SThomas Huth     g_usleep(duration * G_USEC_PER_SEC);
4333b472e71SThomas Huth     qatomic_set(&test_stop, true);
4343b472e71SThomas Huth 
4353b472e71SThomas Huth     for (i = 0; i < n_rw_threads; i++) {
4363b472e71SThomas Huth         qemu_thread_join(&rw_threads[i]);
4373b472e71SThomas Huth     }
4383b472e71SThomas Huth     for (i = 0; i < n_rz_threads; i++) {
4393b472e71SThomas Huth         qemu_thread_join(&rz_threads[i]);
4403b472e71SThomas Huth     }
4413b472e71SThomas Huth }
4423b472e71SThomas Huth 
parse_args(int argc,char * argv[])4433b472e71SThomas Huth static void parse_args(int argc, char *argv[])
4443b472e71SThomas Huth {
4453b472e71SThomas Huth     int c;
4463b472e71SThomas Huth 
4473b472e71SThomas Huth     for (;;) {
4483b472e71SThomas Huth         c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:pr:Rs:S:u:");
4493b472e71SThomas Huth         if (c < 0) {
4503b472e71SThomas Huth             break;
4513b472e71SThomas Huth         }
4523b472e71SThomas Huth         switch (c) {
4533b472e71SThomas Huth         case 'd':
4543b472e71SThomas Huth             duration = atoi(optarg);
4553b472e71SThomas Huth             break;
4563b472e71SThomas Huth         case 'D':
4573b472e71SThomas Huth             resize_delay = atol(optarg);
4583b472e71SThomas Huth             break;
4593b472e71SThomas Huth         case 'g':
4603b472e71SThomas Huth             init_range = pow2ceil(atol(optarg));
4613b472e71SThomas Huth             lookup_range = pow2ceil(atol(optarg));
4623b472e71SThomas Huth             update_range = pow2ceil(atol(optarg));
4633b472e71SThomas Huth             qht_n_elems = atol(optarg);
4643b472e71SThomas Huth             init_size = atol(optarg);
4653b472e71SThomas Huth             break;
4663b472e71SThomas Huth         case 'h':
4673b472e71SThomas Huth             usage_complete(argc, argv);
4683b472e71SThomas Huth             exit(0);
4693b472e71SThomas Huth         case 'k':
4703b472e71SThomas Huth             init_size = atol(optarg);
4713b472e71SThomas Huth             break;
4723b472e71SThomas Huth         case 'K':
4733b472e71SThomas Huth             init_range = pow2ceil(atol(optarg));
4743b472e71SThomas Huth             break;
4753b472e71SThomas Huth         case 'l':
4763b472e71SThomas Huth             lookup_range = pow2ceil(atol(optarg));
4773b472e71SThomas Huth             break;
4783b472e71SThomas Huth         case 'n':
4793b472e71SThomas Huth             n_rw_threads = atoi(optarg);
4803b472e71SThomas Huth             break;
4813b472e71SThomas Huth         case 'N':
4823b472e71SThomas Huth             n_rz_threads = atoi(optarg);
4833b472e71SThomas Huth             break;
4843b472e71SThomas Huth         case 'o':
4853b472e71SThomas Huth             populate_offset = atol(optarg);
4863b472e71SThomas Huth             break;
4873b472e71SThomas Huth         case 'p':
4883b472e71SThomas Huth             precompute_hash = true;
4893b472e71SThomas Huth             hfunc = hval;
4903b472e71SThomas Huth             break;
4913b472e71SThomas Huth         case 'r':
4923b472e71SThomas Huth             update_range = pow2ceil(atol(optarg));
4933b472e71SThomas Huth             break;
4943b472e71SThomas Huth         case 'R':
4953b472e71SThomas Huth             qht_mode |= QHT_MODE_AUTO_RESIZE;
4963b472e71SThomas Huth             break;
4973b472e71SThomas Huth         case 's':
4983b472e71SThomas Huth             qht_n_elems = atol(optarg);
4993b472e71SThomas Huth             break;
5003b472e71SThomas Huth         case 'S':
5013b472e71SThomas Huth             resize_rate = atof(optarg) / 100.0;
5023b472e71SThomas Huth             if (resize_rate > 1.0) {
5033b472e71SThomas Huth                 resize_rate = 1.0;
5043b472e71SThomas Huth             }
5053b472e71SThomas Huth             break;
5063b472e71SThomas Huth         case 'u':
5073b472e71SThomas Huth             update_rate = atof(optarg) / 100.0;
5083b472e71SThomas Huth             if (update_rate > 1.0) {
5093b472e71SThomas Huth                 update_rate = 1.0;
5103b472e71SThomas Huth             }
5113b472e71SThomas Huth             break;
5123b472e71SThomas Huth         }
5133b472e71SThomas Huth     }
5143b472e71SThomas Huth }
5153b472e71SThomas Huth 
main(int argc,char * argv[])5163b472e71SThomas Huth int main(int argc, char *argv[])
5173b472e71SThomas Huth {
5183b472e71SThomas Huth     parse_args(argc, argv);
5193b472e71SThomas Huth     htable_init();
5203b472e71SThomas Huth     create_threads();
5213b472e71SThomas Huth     run_test();
5223b472e71SThomas Huth     pr_stats();
5233b472e71SThomas Huth     return 0;
5243b472e71SThomas Huth }
525