From 3b472e71d50fe33f2e0dfdd447dde5910ddf0761 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Mar 2021 10:22:38 +0100 Subject: tests: Move benchmarks into a separate folder Make it clear that these files are related to benchmarks by moving them into a new folder called "bench". Message-Id: <20210312092238.79509-1-thuth@redhat.com> Acked-by: Paolo Bonzini Signed-off-by: Thomas Huth --- tests/atomic64-bench.c | 169 ----------- tests/atomic_add-bench.c | 180 ------------ tests/bench/atomic64-bench.c | 169 +++++++++++ tests/bench/atomic_add-bench.c | 180 ++++++++++++ tests/bench/benchmark-crypto-cipher.c | 208 ++++++++++++++ tests/bench/benchmark-crypto-hash.c | 116 ++++++++ tests/bench/benchmark-crypto-hmac.c | 81 ++++++ tests/bench/meson.build | 34 +++ tests/bench/qht-bench.c | 523 ++++++++++++++++++++++++++++++++++ tests/benchmark-crypto-cipher.c | 208 -------------- tests/benchmark-crypto-hash.c | 116 -------- tests/benchmark-crypto-hmac.c | 81 ------ tests/meson.build | 34 +-- tests/qht-bench.c | 523 ---------------------------------- 14 files changed, 1312 insertions(+), 1310 deletions(-) delete mode 100644 tests/atomic64-bench.c delete mode 100644 tests/atomic_add-bench.c create mode 100644 tests/bench/atomic64-bench.c create mode 100644 tests/bench/atomic_add-bench.c create mode 100644 tests/bench/benchmark-crypto-cipher.c create mode 100644 tests/bench/benchmark-crypto-hash.c create mode 100644 tests/bench/benchmark-crypto-hmac.c create mode 100644 tests/bench/meson.build create mode 100644 tests/bench/qht-bench.c delete mode 100644 tests/benchmark-crypto-cipher.c delete mode 100644 tests/benchmark-crypto-hash.c delete mode 100644 tests/benchmark-crypto-hmac.c delete mode 100644 tests/qht-bench.c (limited to 'tests') diff --git a/tests/atomic64-bench.c b/tests/atomic64-bench.c deleted file mode 100644 index e474753d34..0000000000 --- a/tests/atomic64-bench.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2018, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/thread.h" -#include "qemu/host-utils.h" -#include "qemu/processor.h" - -struct thread_info { - uint64_t r; - uint64_t accesses; -} QEMU_ALIGNED(64); - -struct count { - int64_t i64; -} QEMU_ALIGNED(64); - -static QemuThread *threads; -static struct thread_info *th_info; -static unsigned int n_threads = 1; -static unsigned int n_ready_threads; -static struct count *counts; -static unsigned int duration = 1; -static unsigned int range = 1024; -static bool test_start; -static bool test_stop; - -static const char commands_string[] = - " -d = duration in seconds\n" - " -n = number of threads\n" - " -r = range (will be rounded up to pow2)"; - -static void usage_complete(char *argv[]) -{ - fprintf(stderr, "Usage: %s [options]\n", argv[0]); - fprintf(stderr, "options:\n%s\n", commands_string); -} - -/* - * From: https://en.wikipedia.org/wiki/Xorshift - * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only - * guaranteed to be >= INT_MAX). - */ -static uint64_t xorshift64star(uint64_t x) -{ - x ^= x >> 12; /* a */ - x ^= x << 25; /* b */ - x ^= x >> 27; /* c */ - return x * UINT64_C(2685821657736338717); -} - -static void *thread_func(void *arg) -{ - struct thread_info *info = arg; - - qatomic_inc(&n_ready_threads); - while (!qatomic_read(&test_start)) { - cpu_relax(); - } - - while (!qatomic_read(&test_stop)) { - unsigned int index; - - info->r = xorshift64star(info->r); - index = info->r & (range - 1); - qatomic_read_i64(&counts[index].i64); - info->accesses++; - } - return NULL; -} - -static void run_test(void) -{ - unsigned int i; - - while (qatomic_read(&n_ready_threads) != n_threads) { - cpu_relax(); - } - - qatomic_set(&test_start, true); - g_usleep(duration * G_USEC_PER_SEC); - qatomic_set(&test_stop, true); - - for (i = 0; i < n_threads; i++) { - qemu_thread_join(&threads[i]); - } -} - -static void create_threads(void) -{ - unsigned int i; - - threads = g_new(QemuThread, n_threads); - th_info = g_new(struct thread_info, n_threads); - counts = g_malloc0_n(range, sizeof(*counts)); - - for (i = 0; i < n_threads; i++) { - struct thread_info *info = &th_info[i]; - - info->r = (i + 1) ^ time(NULL); - info->accesses = 0; - qemu_thread_create(&threads[i], NULL, thread_func, info, - QEMU_THREAD_JOINABLE); - } -} - -static void pr_params(void) -{ - printf("Parameters:\n"); - printf(" # of threads: %u\n", n_threads); - printf(" duration: %u\n", duration); - printf(" ops' range: %u\n", range); -} - -static void pr_stats(void) -{ - unsigned long long val = 0; - double tx; - int i; - - for (i = 0; i < n_threads; i++) { - val += th_info[i].accesses; - } - tx = val / duration / 1e6; - - printf("Results:\n"); - printf("Duration: %u s\n", duration); - printf(" Throughput: %.2f Mops/s\n", tx); - printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - - for (;;) { - c = getopt(argc, argv, "hd:n:r:"); - if (c < 0) { - break; - } - switch (c) { - case 'h': - usage_complete(argv); - exit(0); - case 'd': - duration = atoi(optarg); - break; - case 'n': - n_threads = atoi(optarg); - break; - case 'r': - range = pow2ceil(atoi(optarg)); - break; - } - } -} - -int main(int argc, char *argv[]) -{ - parse_args(argc, argv); - pr_params(); - create_threads(); - run_test(); - pr_stats(); - return 0; -} diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c deleted file mode 100644 index f05471ab45..0000000000 --- a/tests/atomic_add-bench.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/thread.h" -#include "qemu/host-utils.h" -#include "qemu/processor.h" - -struct thread_info { - uint64_t r; -} QEMU_ALIGNED(64); - -struct count { - QemuMutex lock; - unsigned long val; -} QEMU_ALIGNED(64); - -static QemuThread *threads; -static struct thread_info *th_info; -static unsigned int n_threads = 1; -static unsigned int n_ready_threads; -static struct count *counts; -static unsigned int duration = 1; -static unsigned int range = 1024; -static bool use_mutex; -static bool test_start; -static bool test_stop; - -static const char commands_string[] = - " -n = number of threads\n" - " -m = use mutexes instead of atomic increments\n" - " -p = enable sync profiler\n" - " -d = duration in seconds\n" - " -r = range (will be rounded up to pow2)"; - -static void usage_complete(char *argv[]) -{ - fprintf(stderr, "Usage: %s [options]\n", argv[0]); - fprintf(stderr, "options:\n%s\n", commands_string); -} - -/* - * From: https://en.wikipedia.org/wiki/Xorshift - * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only - * guaranteed to be >= INT_MAX). - */ -static uint64_t xorshift64star(uint64_t x) -{ - x ^= x >> 12; /* a */ - x ^= x << 25; /* b */ - x ^= x >> 27; /* c */ - return x * UINT64_C(2685821657736338717); -} - -static void *thread_func(void *arg) -{ - struct thread_info *info = arg; - - qatomic_inc(&n_ready_threads); - while (!qatomic_read(&test_start)) { - cpu_relax(); - } - - while (!qatomic_read(&test_stop)) { - unsigned int index; - - info->r = xorshift64star(info->r); - index = info->r & (range - 1); - if (use_mutex) { - qemu_mutex_lock(&counts[index].lock); - counts[index].val += 1; - qemu_mutex_unlock(&counts[index].lock); - } else { - qatomic_inc(&counts[index].val); - } - } - return NULL; -} - -static void run_test(void) -{ - unsigned int i; - - while (qatomic_read(&n_ready_threads) != n_threads) { - cpu_relax(); - } - - qatomic_set(&test_start, true); - g_usleep(duration * G_USEC_PER_SEC); - qatomic_set(&test_stop, true); - - for (i = 0; i < n_threads; i++) { - qemu_thread_join(&threads[i]); - } -} - -static void create_threads(void) -{ - unsigned int i; - - threads = g_new(QemuThread, n_threads); - th_info = g_new(struct thread_info, n_threads); - counts = qemu_memalign(64, sizeof(*counts) * range); - memset(counts, 0, sizeof(*counts) * range); - for (i = 0; i < range; i++) { - qemu_mutex_init(&counts[i].lock); - } - - for (i = 0; i < n_threads; i++) { - struct thread_info *info = &th_info[i]; - - info->r = (i + 1) ^ time(NULL); - qemu_thread_create(&threads[i], NULL, thread_func, info, - QEMU_THREAD_JOINABLE); - } -} - -static void pr_params(void) -{ - printf("Parameters:\n"); - printf(" # of threads: %u\n", n_threads); - printf(" duration: %u\n", duration); - printf(" ops' range: %u\n", range); -} - -static void pr_stats(void) -{ - unsigned long long val = 0; - unsigned int i; - double tx; - - for (i = 0; i < range; i++) { - val += counts[i].val; - } - tx = val / duration / 1e6; - - printf("Results:\n"); - printf("Duration: %u s\n", duration); - printf(" Throughput: %.2f Mops/s\n", tx); - printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - - for (;;) { - c = getopt(argc, argv, "hd:n:mpr:"); - if (c < 0) { - break; - } - switch (c) { - case 'h': - usage_complete(argv); - exit(0); - case 'd': - duration = atoi(optarg); - break; - case 'n': - n_threads = atoi(optarg); - break; - case 'm': - use_mutex = true; - break; - case 'p': - qsp_enable(); - break; - case 'r': - range = pow2ceil(atoi(optarg)); - break; - } - } -} - -int main(int argc, char *argv[]) -{ - parse_args(argc, argv); - pr_params(); - create_threads(); - run_test(); - pr_stats(); - return 0; -} diff --git a/tests/bench/atomic64-bench.c b/tests/bench/atomic64-bench.c new file mode 100644 index 0000000000..e474753d34 --- /dev/null +++ b/tests/bench/atomic64-bench.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/host-utils.h" +#include "qemu/processor.h" + +struct thread_info { + uint64_t r; + uint64_t accesses; +} QEMU_ALIGNED(64); + +struct count { + int64_t i64; +} QEMU_ALIGNED(64); + +static QemuThread *threads; +static struct thread_info *th_info; +static unsigned int n_threads = 1; +static unsigned int n_ready_threads; +static struct count *counts; +static unsigned int duration = 1; +static unsigned int range = 1024; +static bool test_start; +static bool test_stop; + +static const char commands_string[] = + " -d = duration in seconds\n" + " -n = number of threads\n" + " -r = range (will be rounded up to pow2)"; + +static void usage_complete(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); +} + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void *thread_func(void *arg) +{ + struct thread_info *info = arg; + + qatomic_inc(&n_ready_threads); + while (!qatomic_read(&test_start)) { + cpu_relax(); + } + + while (!qatomic_read(&test_stop)) { + unsigned int index; + + info->r = xorshift64star(info->r); + index = info->r & (range - 1); + qatomic_read_i64(&counts[index].i64); + info->accesses++; + } + return NULL; +} + +static void run_test(void) +{ + unsigned int i; + + while (qatomic_read(&n_ready_threads) != n_threads) { + cpu_relax(); + } + + qatomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + qatomic_set(&test_stop, true); + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } +} + +static void create_threads(void) +{ + unsigned int i; + + threads = g_new(QemuThread, n_threads); + th_info = g_new(struct thread_info, n_threads); + counts = g_malloc0_n(range, sizeof(*counts)); + + for (i = 0; i < n_threads; i++) { + struct thread_info *info = &th_info[i]; + + info->r = (i + 1) ^ time(NULL); + info->accesses = 0; + qemu_thread_create(&threads[i], NULL, thread_func, info, + QEMU_THREAD_JOINABLE); + } +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" # of threads: %u\n", n_threads); + printf(" duration: %u\n", duration); + printf(" ops' range: %u\n", range); +} + +static void pr_stats(void) +{ + unsigned long long val = 0; + double tx; + int i; + + for (i = 0; i < n_threads; i++) { + val += th_info[i].accesses; + } + tx = val / duration / 1e6; + + printf("Results:\n"); + printf("Duration: %u s\n", duration); + printf(" Throughput: %.2f Mops/s\n", tx); + printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "hd:n:r:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argv); + exit(0); + case 'd': + duration = atoi(optarg); + break; + case 'n': + n_threads = atoi(optarg); + break; + case 'r': + range = pow2ceil(atoi(optarg)); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + pr_params(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/bench/atomic_add-bench.c b/tests/bench/atomic_add-bench.c new file mode 100644 index 0000000000..f05471ab45 --- /dev/null +++ b/tests/bench/atomic_add-bench.c @@ -0,0 +1,180 @@ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/host-utils.h" +#include "qemu/processor.h" + +struct thread_info { + uint64_t r; +} QEMU_ALIGNED(64); + +struct count { + QemuMutex lock; + unsigned long val; +} QEMU_ALIGNED(64); + +static QemuThread *threads; +static struct thread_info *th_info; +static unsigned int n_threads = 1; +static unsigned int n_ready_threads; +static struct count *counts; +static unsigned int duration = 1; +static unsigned int range = 1024; +static bool use_mutex; +static bool test_start; +static bool test_stop; + +static const char commands_string[] = + " -n = number of threads\n" + " -m = use mutexes instead of atomic increments\n" + " -p = enable sync profiler\n" + " -d = duration in seconds\n" + " -r = range (will be rounded up to pow2)"; + +static void usage_complete(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); +} + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void *thread_func(void *arg) +{ + struct thread_info *info = arg; + + qatomic_inc(&n_ready_threads); + while (!qatomic_read(&test_start)) { + cpu_relax(); + } + + while (!qatomic_read(&test_stop)) { + unsigned int index; + + info->r = xorshift64star(info->r); + index = info->r & (range - 1); + if (use_mutex) { + qemu_mutex_lock(&counts[index].lock); + counts[index].val += 1; + qemu_mutex_unlock(&counts[index].lock); + } else { + qatomic_inc(&counts[index].val); + } + } + return NULL; +} + +static void run_test(void) +{ + unsigned int i; + + while (qatomic_read(&n_ready_threads) != n_threads) { + cpu_relax(); + } + + qatomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + qatomic_set(&test_stop, true); + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } +} + +static void create_threads(void) +{ + unsigned int i; + + threads = g_new(QemuThread, n_threads); + th_info = g_new(struct thread_info, n_threads); + counts = qemu_memalign(64, sizeof(*counts) * range); + memset(counts, 0, sizeof(*counts) * range); + for (i = 0; i < range; i++) { + qemu_mutex_init(&counts[i].lock); + } + + for (i = 0; i < n_threads; i++) { + struct thread_info *info = &th_info[i]; + + info->r = (i + 1) ^ time(NULL); + qemu_thread_create(&threads[i], NULL, thread_func, info, + QEMU_THREAD_JOINABLE); + } +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" # of threads: %u\n", n_threads); + printf(" duration: %u\n", duration); + printf(" ops' range: %u\n", range); +} + +static void pr_stats(void) +{ + unsigned long long val = 0; + unsigned int i; + double tx; + + for (i = 0; i < range; i++) { + val += counts[i].val; + } + tx = val / duration / 1e6; + + printf("Results:\n"); + printf("Duration: %u s\n", duration); + printf(" Throughput: %.2f Mops/s\n", tx); + printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "hd:n:mpr:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argv); + exit(0); + case 'd': + duration = atoi(optarg); + break; + case 'n': + n_threads = atoi(optarg); + break; + case 'm': + use_mutex = true; + break; + case 'p': + qsp_enable(); + break; + case 'r': + range = pow2ceil(atoi(optarg)); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + pr_params(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/bench/benchmark-crypto-cipher.c b/tests/bench/benchmark-crypto-cipher.c new file mode 100644 index 0000000000..c04f0a0fba --- /dev/null +++ b/tests/bench/benchmark-crypto-cipher.c @@ -0,0 +1,208 @@ +/* + * QEMU Crypto cipher speed benchmark + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "crypto/init.h" +#include "crypto/cipher.h" + +static void test_cipher_speed(size_t chunk_size, + QCryptoCipherMode mode, + QCryptoCipherAlgorithm alg) +{ + QCryptoCipher *cipher; + Error *err = NULL; + uint8_t *key = NULL, *iv = NULL; + uint8_t *plaintext = NULL, *ciphertext = NULL; + size_t nkey; + size_t niv; + const size_t total = 2 * GiB; + size_t remain; + + if (!qcrypto_cipher_supports(alg, mode)) { + return; + } + + nkey = qcrypto_cipher_get_key_len(alg); + niv = qcrypto_cipher_get_iv_len(alg, mode); + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + nkey *= 2; + } + + key = g_new0(uint8_t, nkey); + memset(key, g_test_rand_int(), nkey); + + iv = g_new0(uint8_t, niv); + memset(iv, g_test_rand_int(), niv); + + ciphertext = g_new0(uint8_t, chunk_size); + + plaintext = g_new0(uint8_t, chunk_size); + memset(plaintext, g_test_rand_int(), chunk_size); + + cipher = qcrypto_cipher_new(alg, mode, + key, nkey, &err); + g_assert(cipher != NULL); + + if (mode != QCRYPTO_CIPHER_MODE_ECB) + g_assert(qcrypto_cipher_setiv(cipher, + iv, niv, + &err) == 0); + + g_test_timer_start(); + remain = total; + while (remain) { + g_assert(qcrypto_cipher_encrypt(cipher, + plaintext, + ciphertext, + chunk_size, + &err) == 0); + remain -= chunk_size; + } + g_test_timer_elapsed(); + + g_test_message("enc(%s-%s) chunk %zu bytes %.2f MB/sec ", + QCryptoCipherAlgorithm_str(alg), + QCryptoCipherMode_str(mode), + chunk_size, (double)total / MiB / g_test_timer_last()); + + g_test_timer_start(); + remain = total; + while (remain) { + g_assert(qcrypto_cipher_decrypt(cipher, + plaintext, + ciphertext, + chunk_size, + &err) == 0); + remain -= chunk_size; + } + g_test_timer_elapsed(); + + g_test_message("dec(%s-%s) chunk %zu bytes %.2f MB/sec ", + QCryptoCipherAlgorithm_str(alg), + QCryptoCipherMode_str(mode), + chunk_size, (double)total / MiB / g_test_timer_last()); + + qcrypto_cipher_free(cipher); + g_free(plaintext); + g_free(ciphertext); + g_free(iv); + g_free(key); +} + + +static void test_cipher_speed_ecb_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_ECB, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_ecb_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_ECB, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_cbc_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CBC, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_cbc_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CBC, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_ctr_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CTR, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_ctr_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CTR, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_xts_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_XTS, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_xts_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_XTS, + QCRYPTO_CIPHER_ALG_AES_256); +} + + +int main(int argc, char **argv) +{ + char *alg = NULL; + char *size = NULL; + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +#define ADD_TEST(mode, cipher, keysize, chunk) \ + if ((!alg || g_str_equal(alg, #mode)) && \ + (!size || g_str_equal(size, #chunk))) \ + g_test_add_data_func( \ + "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ + (void *)chunk, \ + test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) + + if (argc >= 2) { + alg = argv[1]; + } + if (argc >= 3) { + size = argv[2]; + } + +#define ADD_TESTS(chunk) \ + do { \ + ADD_TEST(ecb, aes, 128, chunk); \ + ADD_TEST(ecb, aes, 256, chunk); \ + ADD_TEST(cbc, aes, 128, chunk); \ + ADD_TEST(cbc, aes, 256, chunk); \ + ADD_TEST(ctr, aes, 128, chunk); \ + ADD_TEST(ctr, aes, 256, chunk); \ + ADD_TEST(xts, aes, 128, chunk); \ + ADD_TEST(xts, aes, 256, chunk); \ + } while (0) + + ADD_TESTS(512); + ADD_TESTS(4096); + ADD_TESTS(16384); + ADD_TESTS(65536); + + return g_test_run(); +} diff --git a/tests/bench/benchmark-crypto-hash.c b/tests/bench/benchmark-crypto-hash.c new file mode 100644 index 0000000000..927b00bb4d --- /dev/null +++ b/tests/bench/benchmark-crypto-hash.c @@ -0,0 +1,116 @@ +/* + * QEMU Crypto hash speed benchmark + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "crypto/init.h" +#include "crypto/hash.h" + +typedef struct QCryptoHashOpts { + size_t chunk_size; + QCryptoHashAlgorithm alg; +} QCryptoHashOpts; + +static void test_hash_speed(const void *opaque) +{ + const QCryptoHashOpts *opts = opaque; + uint8_t *in = NULL, *out = NULL; + size_t out_len = 0; + const size_t total = 2 * GiB; + size_t remain; + struct iovec iov; + int ret; + + in = g_new0(uint8_t, opts->chunk_size); + memset(in, g_test_rand_int(), opts->chunk_size); + + iov.iov_base = (char *)in; + iov.iov_len = opts->chunk_size; + + g_test_timer_start(); + remain = total; + while (remain) { + ret = qcrypto_hash_bytesv(opts->alg, + &iov, 1, &out, &out_len, + NULL); + g_assert(ret == 0); + + remain -= opts->chunk_size; + } + g_test_timer_elapsed(); + + g_test_message("hash(%s): chunk %zu bytes %.2f MB/sec", + QCryptoHashAlgorithm_str(opts->alg), + opts->chunk_size, total / g_test_timer_last()); + + g_free(out); + g_free(in); +} + +int main(int argc, char **argv) +{ + char name[64]; + + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +#define TEST_ONE(a, c) \ + QCryptoHashOpts opts ## a ## c = { \ + .alg = QCRYPTO_HASH_ALG_ ## a, .chunk_size = c, \ + }; \ + memset(name, 0 , sizeof(name)); \ + snprintf(name, sizeof(name), \ + "/crypto/benchmark/hash/%s/bufsize-%d", \ + QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_ ## a), \ + c); \ + if (qcrypto_hash_supports(QCRYPTO_HASH_ALG_ ## a)) \ + g_test_add_data_func(name, \ + &opts ## a ## c, \ + test_hash_speed); + + TEST_ONE(MD5, 512); + TEST_ONE(MD5, 1024); + TEST_ONE(MD5, 4096); + TEST_ONE(MD5, 16384); + + TEST_ONE(SHA1, 512); + TEST_ONE(SHA1, 1024); + TEST_ONE(SHA1, 4096); + TEST_ONE(SHA1, 16384); + + TEST_ONE(SHA224, 512); + TEST_ONE(SHA224, 1024); + TEST_ONE(SHA224, 4096); + TEST_ONE(SHA224, 16384); + + TEST_ONE(SHA384, 512); + TEST_ONE(SHA384, 1024); + TEST_ONE(SHA384, 4096); + TEST_ONE(SHA384, 16384); + + TEST_ONE(SHA256, 512); + TEST_ONE(SHA256, 1024); + TEST_ONE(SHA256, 4096); + TEST_ONE(SHA256, 16384); + + TEST_ONE(SHA512, 512); + TEST_ONE(SHA512, 1024); + TEST_ONE(SHA512, 4096); + TEST_ONE(SHA512, 16384); + + TEST_ONE(RIPEMD160, 512); + TEST_ONE(RIPEMD160, 1024); + TEST_ONE(RIPEMD160, 4096); + TEST_ONE(RIPEMD160, 16384); + + return g_test_run(); +} diff --git a/tests/bench/benchmark-crypto-hmac.c b/tests/bench/benchmark-crypto-hmac.c new file mode 100644 index 0000000000..5cca636789 --- /dev/null +++ b/tests/bench/benchmark-crypto-hmac.c @@ -0,0 +1,81 @@ +/* + * QEMU Crypto hmac speed benchmark + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "crypto/init.h" +#include "crypto/hmac.h" + +#define KEY "monkey monkey monkey monkey" + +static void test_hmac_speed(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + QCryptoHmac *hmac = NULL; + uint8_t *in = NULL, *out = NULL; + size_t out_len = 0; + double total = 0.0; + struct iovec iov; + Error *err = NULL; + int ret; + + if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALG_SHA256)) { + return; + } + + in = g_new0(uint8_t, chunk_size); + memset(in, g_test_rand_int(), chunk_size); + + iov.iov_base = (char *)in; + iov.iov_len = chunk_size; + + g_test_timer_start(); + do { + hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALG_SHA256, + (const uint8_t *)KEY, strlen(KEY), &err); + g_assert(err == NULL); + g_assert(hmac != NULL); + + ret = qcrypto_hmac_bytesv(hmac, &iov, 1, &out, &out_len, &err); + g_assert(ret == 0); + g_assert(err == NULL); + + qcrypto_hmac_free(hmac); + + total += chunk_size; + } while (g_test_timer_elapsed() < 5.0); + + total /= MiB; + g_test_message("hmac(%s): chunk %zu bytes %.2f MB/sec", + QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_SHA256), + chunk_size, total / g_test_timer_last()); + + g_free(out); + g_free(in); +} + +int main(int argc, char **argv) +{ + size_t i; + char name[64]; + + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + + for (i = 512; i <= 64 * KiB; i *= 2) { + memset(name, 0 , sizeof(name)); + snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); + g_test_add_data_func(name, (void *)i, test_hmac_speed); + } + + return g_test_run(); +} diff --git a/tests/bench/meson.build b/tests/bench/meson.build new file mode 100644 index 0000000000..00b3c209dc --- /dev/null +++ b/tests/bench/meson.build @@ -0,0 +1,34 @@ + +qht_bench = executable('qht-bench', + sources: 'qht-bench.c', + dependencies: [qemuutil]) + +executable('atomic_add-bench', + sources: files('atomic_add-bench.c'), + dependencies: [qemuutil], + build_by_default: false) + +executable('atomic64-bench', + sources: files('atomic64-bench.c'), + dependencies: [qemuutil], + build_by_default: false) + +benchs = {} + +if have_block + benchs += { + 'benchmark-crypto-hash': [crypto], + 'benchmark-crypto-hmac': [crypto], + 'benchmark-crypto-cipher': [crypto], + } +endif + +foreach bench_name, deps: benchs + exe = executable(bench_name, bench_name + '.c', + dependencies: [qemuutil] + deps) + benchmark(bench_name, exe, + args: ['--tap', '-k'], + protocol: 'tap', + timeout: 0, + suite: ['speed']) +endforeach diff --git a/tests/bench/qht-bench.c b/tests/bench/qht-bench.c new file mode 100644 index 0000000000..2e5b70ccd0 --- /dev/null +++ b/tests/bench/qht-bench.c @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2016, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/processor.h" +#include "qemu/atomic.h" +#include "qemu/qht.h" +#include "qemu/rcu.h" +#include "qemu/xxhash.h" + +struct thread_stats { + size_t rd; + size_t not_rd; + size_t in; + size_t not_in; + size_t rm; + size_t not_rm; + size_t rz; + size_t not_rz; +}; + +struct thread_info { + void (*func)(struct thread_info *); + struct thread_stats stats; + /* + * Seed is in the range [1..UINT64_MAX], because the RNG requires + * a non-zero seed. To use, subtract 1 and compare against the + * threshold with =. This lets threshold = 0 never match (0% hit), + * and threshold = UINT64_MAX always match (100% hit). + */ + uint64_t seed; + bool write_op; /* writes alternate between insertions and removals */ + bool resize_down; +} QEMU_ALIGNED(64); /* avoid false sharing among threads */ + +static struct qht ht; +static QemuThread *rw_threads; + +#define DEFAULT_RANGE (4096) +#define DEFAULT_QHT_N_ELEMS DEFAULT_RANGE + +static unsigned int duration = 1; +static unsigned int n_rw_threads = 1; +static unsigned long lookup_range = DEFAULT_RANGE; +static unsigned long update_range = DEFAULT_RANGE; +static size_t init_range = DEFAULT_RANGE; +static size_t init_size = DEFAULT_RANGE; +static size_t n_ready_threads; +static long populate_offset; +static long *keys; + +static size_t resize_min; +static size_t resize_max; +static struct thread_info *rz_info; +static unsigned long resize_delay = 1000; +static double resize_rate; /* 0.0 to 1.0 */ +static unsigned int n_rz_threads = 1; +static QemuThread *rz_threads; +static bool precompute_hash; + +static double update_rate; /* 0.0 to 1.0 */ +static uint64_t update_threshold; +static uint64_t resize_threshold; + +static size_t qht_n_elems = DEFAULT_QHT_N_ELEMS; +static int qht_mode; + +static bool test_start; +static bool test_stop; + +static struct thread_info *rw_info; + +static const char commands_string[] = + " -d = duration, in seconds\n" + " -n = number of threads\n" + "\n" + " -o = offset at which keys start\n" + " -p = precompute hashes\n" + "\n" + " -g = set -s,-k,-K,-l,-r to the same value\n" + " -s = initial size hint\n" + " -k = initial number of keys\n" + " -K = initial range of keys (will be rounded up to pow2)\n" + " -l = lookup range of keys (will be rounded up to pow2)\n" + " -r = update range of keys (will be rounded up to pow2)\n" + "\n" + " -u = update rate (0.0 to 100.0), 50/50 split of insertions/removals\n" + "\n" + " -R = enable auto-resize\n" + " -S = resize rate (0.0 to 100.0)\n" + " -D = delay (in us) between potential resizes\n" + " -N = number of resize threads"; + +static void usage_complete(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); + exit(-1); +} + +static bool is_equal(const void *ap, const void *bp) +{ + const long *a = ap; + const long *b = bp; + + return *a == *b; +} + +static uint32_t h(unsigned long v) +{ + return qemu_xxhash2(v); +} + +static uint32_t hval(unsigned long v) +{ + return v; +} + +static uint32_t (*hfunc)(unsigned long v) = h; + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void do_rz(struct thread_info *info) +{ + struct thread_stats *stats = &info->stats; + uint64_t r = info->seed - 1; + + if (r < resize_threshold) { + size_t size = info->resize_down ? resize_min : resize_max; + bool resized; + + resized = qht_resize(&ht, size); + info->resize_down = !info->resize_down; + + if (resized) { + stats->rz++; + } else { + stats->not_rz++; + } + } + g_usleep(resize_delay); +} + +static void do_rw(struct thread_info *info) +{ + struct thread_stats *stats = &info->stats; + uint64_t r = info->seed - 1; + uint32_t hash; + long *p; + + if (r >= update_threshold) { + bool read; + + p = &keys[r & (lookup_range - 1)]; + hash = hfunc(*p); + read = qht_lookup(&ht, p, hash); + if (read) { + stats->rd++; + } else { + stats->not_rd++; + } + } else { + p = &keys[r & (update_range - 1)]; + hash = hfunc(*p); + if (info->write_op) { + bool written = false; + + if (qht_lookup(&ht, p, hash) == NULL) { + written = qht_insert(&ht, p, hash, NULL); + } + if (written) { + stats->in++; + } else { + stats->not_in++; + } + } else { + bool removed = false; + + if (qht_lookup(&ht, p, hash)) { + removed = qht_remove(&ht, p, hash); + } + if (removed) { + stats->rm++; + } else { + stats->not_rm++; + } + } + info->write_op = !info->write_op; + } +} + +static void *thread_func(void *p) +{ + struct thread_info *info = p; + + rcu_register_thread(); + + qatomic_inc(&n_ready_threads); + while (!qatomic_read(&test_start)) { + cpu_relax(); + } + + rcu_read_lock(); + while (!qatomic_read(&test_stop)) { + info->seed = xorshift64star(info->seed); + info->func(info); + } + rcu_read_unlock(); + + rcu_unregister_thread(); + return NULL; +} + +/* sets everything except info->func */ +static void prepare_thread_info(struct thread_info *info, int i) +{ + /* seed for the RNG; each thread should have a different one */ + info->seed = (i + 1) ^ time(NULL); + /* the first update will be a write */ + info->write_op = true; + /* the first resize will be down */ + info->resize_down = true; + + memset(&info->stats, 0, sizeof(info->stats)); +} + +static void +th_create_n(QemuThread **threads, struct thread_info **infos, const char *name, + void (*func)(struct thread_info *), int offset, int n) +{ + struct thread_info *info; + QemuThread *th; + int i; + + th = g_malloc(sizeof(*th) * n); + *threads = th; + + info = qemu_memalign(64, sizeof(*info) * n); + *infos = info; + + for (i = 0; i < n; i++) { + prepare_thread_info(&info[i], offset + i); + info[i].func = func; + qemu_thread_create(&th[i], name, thread_func, &info[i], + QEMU_THREAD_JOINABLE); + } +} + +static void create_threads(void) +{ + th_create_n(&rw_threads, &rw_info, "rw", do_rw, 0, n_rw_threads); + th_create_n(&rz_threads, &rz_info, "rz", do_rz, n_rw_threads, n_rz_threads); +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" duration: %d s\n", duration); + printf(" # of threads: %u\n", n_rw_threads); + printf(" initial # of keys: %zu\n", init_size); + printf(" initial size hint: %zu\n", qht_n_elems); + printf(" auto-resize: %s\n", + qht_mode & QHT_MODE_AUTO_RESIZE ? "on" : "off"); + if (resize_rate) { + printf(" resize_rate: %f%%\n", resize_rate * 100.0); + printf(" resize range: %zu-%zu\n", resize_min, resize_max); + printf(" # resize threads %u\n", n_rz_threads); + } + printf(" update rate: %f%%\n", update_rate * 100.0); + printf(" offset: %ld\n", populate_offset); + printf(" initial key range: %zu\n", init_range); + printf(" lookup range: %lu\n", lookup_range); + printf(" update range: %lu\n", update_range); +} + +static void do_threshold(double rate, uint64_t *threshold) +{ + /* + * For 0 <= rate <= 1, scale to fit in a uint64_t. + * + * Scale by 2**64, with a special case for 1.0. + * The remainder of the possible values are scattered between 0 + * and 0xfffffffffffff800 (nextafter(0x1p64, 0)). + * + * Note that we cannot simply scale by UINT64_MAX, because that + * value is not representable as an IEEE double value. + * + * If we scale by the next largest value, nextafter(0x1p64, 0), + * then the remainder of the possible values are scattered between + * 0 and 0xfffffffffffff000. Which leaves us with a gap between + * the final two inputs that is twice as large as any other. + */ + if (rate == 1.0) { + *threshold = UINT64_MAX; + } else { + *threshold = rate * 0x1p64; + } +} + +static void htable_init(void) +{ + unsigned long n = MAX(init_range, update_range); + uint64_t r = time(NULL); + size_t retries = 0; + size_t i; + + /* avoid allocating memory later by allocating all the keys now */ + keys = g_malloc(sizeof(*keys) * n); + for (i = 0; i < n; i++) { + long val = populate_offset + i; + + keys[i] = precompute_hash ? h(val) : hval(val); + } + + /* some sanity checks */ + g_assert_cmpuint(lookup_range, <=, n); + + /* compute thresholds */ + do_threshold(update_rate, &update_threshold); + do_threshold(resize_rate, &resize_threshold); + + if (resize_rate) { + resize_min = n / 2; + resize_max = n; + assert(resize_min < resize_max); + } else { + n_rz_threads = 0; + } + + /* initialize the hash table */ + qht_init(&ht, is_equal, qht_n_elems, qht_mode); + assert(init_size <= init_range); + + pr_params(); + + fprintf(stderr, "Initialization: populating %zu items...", init_size); + for (i = 0; i < init_size; i++) { + for (;;) { + uint32_t hash; + long *p; + + r = xorshift64star(r); + p = &keys[r & (init_range - 1)]; + hash = hfunc(*p); + if (qht_insert(&ht, p, hash, NULL)) { + break; + } + retries++; + } + } + fprintf(stderr, " populated after %zu retries\n", retries); +} + +static void add_stats(struct thread_stats *s, struct thread_info *info, int n) +{ + int i; + + for (i = 0; i < n; i++) { + struct thread_stats *stats = &info[i].stats; + + s->rd += stats->rd; + s->not_rd += stats->not_rd; + + s->in += stats->in; + s->not_in += stats->not_in; + + s->rm += stats->rm; + s->not_rm += stats->not_rm; + + s->rz += stats->rz; + s->not_rz += stats->not_rz; + } +} + +static void pr_stats(void) +{ + struct thread_stats s = {}; + double tx; + + add_stats(&s, rw_info, n_rw_threads); + add_stats(&s, rz_info, n_rz_threads); + + printf("Results:\n"); + + if (resize_rate) { + printf(" Resizes: %zu (%.2f%% of %zu)\n", + s.rz, (double)s.rz / (s.rz + s.not_rz) * 100, s.rz + s.not_rz); + } + + printf(" Read: %.2f M (%.2f%% of %.2fM)\n", + (double)s.rd / 1e6, + (double)s.rd / (s.rd + s.not_rd) * 100, + (double)(s.rd + s.not_rd) / 1e6); + printf(" Inserted: %.2f M (%.2f%% of %.2fM)\n", + (double)s.in / 1e6, + (double)s.in / (s.in + s.not_in) * 100, + (double)(s.in + s.not_in) / 1e6); + printf(" Removed: %.2f M (%.2f%% of %.2fM)\n", + (double)s.rm / 1e6, + (double)s.rm / (s.rm + s.not_rm) * 100, + (double)(s.rm + s.not_rm) / 1e6); + + tx = (s.rd + s.not_rd + s.in + s.not_in + s.rm + s.not_rm) / 1e6 / duration; + printf(" Throughput: %.2f MT/s\n", tx); + printf(" Throughput/thread: %.2f MT/s/thread\n", tx / n_rw_threads); +} + +static void run_test(void) +{ + int i; + + while (qatomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) { + cpu_relax(); + } + + qatomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + qatomic_set(&test_stop, true); + + for (i = 0; i < n_rw_threads; i++) { + qemu_thread_join(&rw_threads[i]); + } + for (i = 0; i < n_rz_threads; i++) { + qemu_thread_join(&rz_threads[i]); + } +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:pr:Rs:S:u:"); + if (c < 0) { + break; + } + switch (c) { + case 'd': + duration = atoi(optarg); + break; + case 'D': + resize_delay = atol(optarg); + break; + case 'g': + init_range = pow2ceil(atol(optarg)); + lookup_range = pow2ceil(atol(optarg)); + update_range = pow2ceil(atol(optarg)); + qht_n_elems = atol(optarg); + init_size = atol(optarg); + break; + case 'h': + usage_complete(argc, argv); + exit(0); + case 'k': + init_size = atol(optarg); + break; + case 'K': + init_range = pow2ceil(atol(optarg)); + break; + case 'l': + lookup_range = pow2ceil(atol(optarg)); + break; + case 'n': + n_rw_threads = atoi(optarg); + break; + case 'N': + n_rz_threads = atoi(optarg); + break; + case 'o': + populate_offset = atol(optarg); + break; + case 'p': + precompute_hash = true; + hfunc = hval; + break; + case 'r': + update_range = pow2ceil(atol(optarg)); + break; + case 'R': + qht_mode |= QHT_MODE_AUTO_RESIZE; + break; + case 's': + qht_n_elems = atol(optarg); + break; + case 'S': + resize_rate = atof(optarg) / 100.0; + if (resize_rate > 1.0) { + resize_rate = 1.0; + } + break; + case 'u': + update_rate = atof(optarg) / 100.0; + if (update_rate > 1.0) { + update_rate = 1.0; + } + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + htable_init(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c deleted file mode 100644 index c04f0a0fba..0000000000 --- a/tests/benchmark-crypto-cipher.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * QEMU Crypto cipher speed benchmark - * - * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "crypto/init.h" -#include "crypto/cipher.h" - -static void test_cipher_speed(size_t chunk_size, - QCryptoCipherMode mode, - QCryptoCipherAlgorithm alg) -{ - QCryptoCipher *cipher; - Error *err = NULL; - uint8_t *key = NULL, *iv = NULL; - uint8_t *plaintext = NULL, *ciphertext = NULL; - size_t nkey; - size_t niv; - const size_t total = 2 * GiB; - size_t remain; - - if (!qcrypto_cipher_supports(alg, mode)) { - return; - } - - nkey = qcrypto_cipher_get_key_len(alg); - niv = qcrypto_cipher_get_iv_len(alg, mode); - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - nkey *= 2; - } - - key = g_new0(uint8_t, nkey); - memset(key, g_test_rand_int(), nkey); - - iv = g_new0(uint8_t, niv); - memset(iv, g_test_rand_int(), niv); - - ciphertext = g_new0(uint8_t, chunk_size); - - plaintext = g_new0(uint8_t, chunk_size); - memset(plaintext, g_test_rand_int(), chunk_size); - - cipher = qcrypto_cipher_new(alg, mode, - key, nkey, &err); - g_assert(cipher != NULL); - - if (mode != QCRYPTO_CIPHER_MODE_ECB) - g_assert(qcrypto_cipher_setiv(cipher, - iv, niv, - &err) == 0); - - g_test_timer_start(); - remain = total; - while (remain) { - g_assert(qcrypto_cipher_encrypt(cipher, - plaintext, - ciphertext, - chunk_size, - &err) == 0); - remain -= chunk_size; - } - g_test_timer_elapsed(); - - g_test_message("enc(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), - QCryptoCipherMode_str(mode), - chunk_size, (double)total / MiB / g_test_timer_last()); - - g_test_timer_start(); - remain = total; - while (remain) { - g_assert(qcrypto_cipher_decrypt(cipher, - plaintext, - ciphertext, - chunk_size, - &err) == 0); - remain -= chunk_size; - } - g_test_timer_elapsed(); - - g_test_message("dec(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), - QCryptoCipherMode_str(mode), - chunk_size, (double)total / MiB / g_test_timer_last()); - - qcrypto_cipher_free(cipher); - g_free(plaintext); - g_free(ciphertext); - g_free(iv); - g_free(key); -} - - -static void test_cipher_speed_ecb_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_ecb_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_256); -} - -static void test_cipher_speed_cbc_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_cbc_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_256); -} - -static void test_cipher_speed_ctr_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_ctr_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_256); -} - -static void test_cipher_speed_xts_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_xts_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_256); -} - - -int main(int argc, char **argv) -{ - char *alg = NULL; - char *size = NULL; - g_test_init(&argc, &argv, NULL); - g_assert(qcrypto_init(NULL) == 0); - -#define ADD_TEST(mode, cipher, keysize, chunk) \ - if ((!alg || g_str_equal(alg, #mode)) && \ - (!size || g_str_equal(size, #chunk))) \ - g_test_add_data_func( \ - "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ - (void *)chunk, \ - test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) - - if (argc >= 2) { - alg = argv[1]; - } - if (argc >= 3) { - size = argv[2]; - } - -#define ADD_TESTS(chunk) \ - do { \ - ADD_TEST(ecb, aes, 128, chunk); \ - ADD_TEST(ecb, aes, 256, chunk); \ - ADD_TEST(cbc, aes, 128, chunk); \ - ADD_TEST(cbc, aes, 256, chunk); \ - ADD_TEST(ctr, aes, 128, chunk); \ - ADD_TEST(ctr, aes, 256, chunk); \ - ADD_TEST(xts, aes, 128, chunk); \ - ADD_TEST(xts, aes, 256, chunk); \ - } while (0) - - ADD_TESTS(512); - ADD_TESTS(4096); - ADD_TESTS(16384); - ADD_TESTS(65536); - - return g_test_run(); -} diff --git a/tests/benchmark-crypto-hash.c b/tests/benchmark-crypto-hash.c deleted file mode 100644 index 927b00bb4d..0000000000 --- a/tests/benchmark-crypto-hash.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * QEMU Crypto hash speed benchmark - * - * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "crypto/init.h" -#include "crypto/hash.h" - -typedef struct QCryptoHashOpts { - size_t chunk_size; - QCryptoHashAlgorithm alg; -} QCryptoHashOpts; - -static void test_hash_speed(const void *opaque) -{ - const QCryptoHashOpts *opts = opaque; - uint8_t *in = NULL, *out = NULL; - size_t out_len = 0; - const size_t total = 2 * GiB; - size_t remain; - struct iovec iov; - int ret; - - in = g_new0(uint8_t, opts->chunk_size); - memset(in, g_test_rand_int(), opts->chunk_size); - - iov.iov_base = (char *)in; - iov.iov_len = opts->chunk_size; - - g_test_timer_start(); - remain = total; - while (remain) { - ret = qcrypto_hash_bytesv(opts->alg, - &iov, 1, &out, &out_len, - NULL); - g_assert(ret == 0); - - remain -= opts->chunk_size; - } - g_test_timer_elapsed(); - - g_test_message("hash(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(opts->alg), - opts->chunk_size, total / g_test_timer_last()); - - g_free(out); - g_free(in); -} - -int main(int argc, char **argv) -{ - char name[64]; - - g_test_init(&argc, &argv, NULL); - g_assert(qcrypto_init(NULL) == 0); - -#define TEST_ONE(a, c) \ - QCryptoHashOpts opts ## a ## c = { \ - .alg = QCRYPTO_HASH_ALG_ ## a, .chunk_size = c, \ - }; \ - memset(name, 0 , sizeof(name)); \ - snprintf(name, sizeof(name), \ - "/crypto/benchmark/hash/%s/bufsize-%d", \ - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_ ## a), \ - c); \ - if (qcrypto_hash_supports(QCRYPTO_HASH_ALG_ ## a)) \ - g_test_add_data_func(name, \ - &opts ## a ## c, \ - test_hash_speed); - - TEST_ONE(MD5, 512); - TEST_ONE(MD5, 1024); - TEST_ONE(MD5, 4096); - TEST_ONE(MD5, 16384); - - TEST_ONE(SHA1, 512); - TEST_ONE(SHA1, 1024); - TEST_ONE(SHA1, 4096); - TEST_ONE(SHA1, 16384); - - TEST_ONE(SHA224, 512); - TEST_ONE(SHA224, 1024); - TEST_ONE(SHA224, 4096); - TEST_ONE(SHA224, 16384); - - TEST_ONE(SHA384, 512); - TEST_ONE(SHA384, 1024); - TEST_ONE(SHA384, 4096); - TEST_ONE(SHA384, 16384); - - TEST_ONE(SHA256, 512); - TEST_ONE(SHA256, 1024); - TEST_ONE(SHA256, 4096); - TEST_ONE(SHA256, 16384); - - TEST_ONE(SHA512, 512); - TEST_ONE(SHA512, 1024); - TEST_ONE(SHA512, 4096); - TEST_ONE(SHA512, 16384); - - TEST_ONE(RIPEMD160, 512); - TEST_ONE(RIPEMD160, 1024); - TEST_ONE(RIPEMD160, 4096); - TEST_ONE(RIPEMD160, 16384); - - return g_test_run(); -} diff --git a/tests/benchmark-crypto-hmac.c b/tests/benchmark-crypto-hmac.c deleted file mode 100644 index 5cca636789..0000000000 --- a/tests/benchmark-crypto-hmac.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * QEMU Crypto hmac speed benchmark - * - * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "crypto/init.h" -#include "crypto/hmac.h" - -#define KEY "monkey monkey monkey monkey" - -static void test_hmac_speed(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - QCryptoHmac *hmac = NULL; - uint8_t *in = NULL, *out = NULL; - size_t out_len = 0; - double total = 0.0; - struct iovec iov; - Error *err = NULL; - int ret; - - if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALG_SHA256)) { - return; - } - - in = g_new0(uint8_t, chunk_size); - memset(in, g_test_rand_int(), chunk_size); - - iov.iov_base = (char *)in; - iov.iov_len = chunk_size; - - g_test_timer_start(); - do { - hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALG_SHA256, - (const uint8_t *)KEY, strlen(KEY), &err); - g_assert(err == NULL); - g_assert(hmac != NULL); - - ret = qcrypto_hmac_bytesv(hmac, &iov, 1, &out, &out_len, &err); - g_assert(ret == 0); - g_assert(err == NULL); - - qcrypto_hmac_free(hmac); - - total += chunk_size; - } while (g_test_timer_elapsed() < 5.0); - - total /= MiB; - g_test_message("hmac(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_SHA256), - chunk_size, total / g_test_timer_last()); - - g_free(out); - g_free(in); -} - -int main(int argc, char **argv) -{ - size_t i; - char name[64]; - - g_test_init(&argc, &argv, NULL); - g_assert(qcrypto_init(NULL) == 0); - - for (i = 512; i <= 64 * KiB; i *= 2) { - memset(name, 0 , sizeof(name)); - snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); - g_test_add_data_func(name, (void *)i, test_hmac_speed); - } - - return g_test_run(); -} diff --git a/tests/meson.build b/tests/meson.build index af43fd1eaf..55a7b08275 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,18 +1,6 @@ py3 = import('python').find_installation() -qht_bench = executable('qht-bench', - sources: 'qht-bench.c', - dependencies: [qemuutil]) - -executable('atomic_add-bench', - sources: files('atomic_add-bench.c'), - dependencies: [qemuutil], - build_by_default: false) - -executable('atomic64-bench', - sources: files('atomic64-bench.c'), - dependencies: [qemuutil], - build_by_default: false) +subdir('bench') test_qapi_outputs = [ 'qapi-builtin-types.c', @@ -73,26 +61,6 @@ test_deps = { 'test-qht-par': qht_bench, } -benchs = {} - -if have_block - benchs += { - 'benchmark-crypto-hash': [crypto], - 'benchmark-crypto-hmac': [crypto], - 'benchmark-crypto-cipher': [crypto], - } -endif - -foreach bench_name, deps: benchs - exe = executable(bench_name, bench_name + '.c', - dependencies: [qemuutil] + deps) - benchmark(bench_name, exe, - args: ['--tap', '-k'], - protocol: 'tap', - timeout: 0, - suite: ['speed']) -endforeach - if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in config_host executable('vhost-user-bridge', sources: files('vhost-user-bridge.c'), diff --git a/tests/qht-bench.c b/tests/qht-bench.c deleted file mode 100644 index 2e5b70ccd0..0000000000 --- a/tests/qht-bench.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright (C) 2016, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/processor.h" -#include "qemu/atomic.h" -#include "qemu/qht.h" -#include "qemu/rcu.h" -#include "qemu/xxhash.h" - -struct thread_stats { - size_t rd; - size_t not_rd; - size_t in; - size_t not_in; - size_t rm; - size_t not_rm; - size_t rz; - size_t not_rz; -}; - -struct thread_info { - void (*func)(struct thread_info *); - struct thread_stats stats; - /* - * Seed is in the range [1..UINT64_MAX], because the RNG requires - * a non-zero seed. To use, subtract 1 and compare against the - * threshold with =. This lets threshold = 0 never match (0% hit), - * and threshold = UINT64_MAX always match (100% hit). - */ - uint64_t seed; - bool write_op; /* writes alternate between insertions and removals */ - bool resize_down; -} QEMU_ALIGNED(64); /* avoid false sharing among threads */ - -static struct qht ht; -static QemuThread *rw_threads; - -#define DEFAULT_RANGE (4096) -#define DEFAULT_QHT_N_ELEMS DEFAULT_RANGE - -static unsigned int duration = 1; -static unsigned int n_rw_threads = 1; -static unsigned long lookup_range = DEFAULT_RANGE; -static unsigned long update_range = DEFAULT_RANGE; -static size_t init_range = DEFAULT_RANGE; -static size_t init_size = DEFAULT_RANGE; -static size_t n_ready_threads; -static long populate_offset; -static long *keys; - -static size_t resize_min; -static size_t resize_max; -static struct thread_info *rz_info; -static unsigned long resize_delay = 1000; -static double resize_rate; /* 0.0 to 1.0 */ -static unsigned int n_rz_threads = 1; -static QemuThread *rz_threads; -static bool precompute_hash; - -static double update_rate; /* 0.0 to 1.0 */ -static uint64_t update_threshold; -static uint64_t resize_threshold; - -static size_t qht_n_elems = DEFAULT_QHT_N_ELEMS; -static int qht_mode; - -static bool test_start; -static bool test_stop; - -static struct thread_info *rw_info; - -static const char commands_string[] = - " -d = duration, in seconds\n" - " -n = number of threads\n" - "\n" - " -o = offset at which keys start\n" - " -p = precompute hashes\n" - "\n" - " -g = set -s,-k,-K,-l,-r to the same value\n" - " -s = initial size hint\n" - " -k = initial number of keys\n" - " -K = initial range of keys (will be rounded up to pow2)\n" - " -l = lookup range of keys (will be rounded up to pow2)\n" - " -r = update range of keys (will be rounded up to pow2)\n" - "\n" - " -u = update rate (0.0 to 100.0), 50/50 split of insertions/removals\n" - "\n" - " -R = enable auto-resize\n" - " -S = resize rate (0.0 to 100.0)\n" - " -D = delay (in us) between potential resizes\n" - " -N = number of resize threads"; - -static void usage_complete(int argc, char *argv[]) -{ - fprintf(stderr, "Usage: %s [options]\n", argv[0]); - fprintf(stderr, "options:\n%s\n", commands_string); - exit(-1); -} - -static bool is_equal(const void *ap, const void *bp) -{ - const long *a = ap; - const long *b = bp; - - return *a == *b; -} - -static uint32_t h(unsigned long v) -{ - return qemu_xxhash2(v); -} - -static uint32_t hval(unsigned long v) -{ - return v; -} - -static uint32_t (*hfunc)(unsigned long v) = h; - -/* - * From: https://en.wikipedia.org/wiki/Xorshift - * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only - * guaranteed to be >= INT_MAX). - */ -static uint64_t xorshift64star(uint64_t x) -{ - x ^= x >> 12; /* a */ - x ^= x << 25; /* b */ - x ^= x >> 27; /* c */ - return x * UINT64_C(2685821657736338717); -} - -static void do_rz(struct thread_info *info) -{ - struct thread_stats *stats = &info->stats; - uint64_t r = info->seed - 1; - - if (r < resize_threshold) { - size_t size = info->resize_down ? resize_min : resize_max; - bool resized; - - resized = qht_resize(&ht, size); - info->resize_down = !info->resize_down; - - if (resized) { - stats->rz++; - } else { - stats->not_rz++; - } - } - g_usleep(resize_delay); -} - -static void do_rw(struct thread_info *info) -{ - struct thread_stats *stats = &info->stats; - uint64_t r = info->seed - 1; - uint32_t hash; - long *p; - - if (r >= update_threshold) { - bool read; - - p = &keys[r & (lookup_range - 1)]; - hash = hfunc(*p); - read = qht_lookup(&ht, p, hash); - if (read) { - stats->rd++; - } else { - stats->not_rd++; - } - } else { - p = &keys[r & (update_range - 1)]; - hash = hfunc(*p); - if (info->write_op) { - bool written = false; - - if (qht_lookup(&ht, p, hash) == NULL) { - written = qht_insert(&ht, p, hash, NULL); - } - if (written) { - stats->in++; - } else { - stats->not_in++; - } - } else { - bool removed = false; - - if (qht_lookup(&ht, p, hash)) { - removed = qht_remove(&ht, p, hash); - } - if (removed) { - stats->rm++; - } else { - stats->not_rm++; - } - } - info->write_op = !info->write_op; - } -} - -static void *thread_func(void *p) -{ - struct thread_info *info = p; - - rcu_register_thread(); - - qatomic_inc(&n_ready_threads); - while (!qatomic_read(&test_start)) { - cpu_relax(); - } - - rcu_read_lock(); - while (!qatomic_read(&test_stop)) { - info->seed = xorshift64star(info->seed); - info->func(info); - } - rcu_read_unlock(); - - rcu_unregister_thread(); - return NULL; -} - -/* sets everything except info->func */ -static void prepare_thread_info(struct thread_info *info, int i) -{ - /* seed for the RNG; each thread should have a different one */ - info->seed = (i + 1) ^ time(NULL); - /* the first update will be a write */ - info->write_op = true; - /* the first resize will be down */ - info->resize_down = true; - - memset(&info->stats, 0, sizeof(info->stats)); -} - -static void -th_create_n(QemuThread **threads, struct thread_info **infos, const char *name, - void (*func)(struct thread_info *), int offset, int n) -{ - struct thread_info *info; - QemuThread *th; - int i; - - th = g_malloc(sizeof(*th) * n); - *threads = th; - - info = qemu_memalign(64, sizeof(*info) * n); - *infos = info; - - for (i = 0; i < n; i++) { - prepare_thread_info(&info[i], offset + i); - info[i].func = func; - qemu_thread_create(&th[i], name, thread_func, &info[i], - QEMU_THREAD_JOINABLE); - } -} - -static void create_threads(void) -{ - th_create_n(&rw_threads, &rw_info, "rw", do_rw, 0, n_rw_threads); - th_create_n(&rz_threads, &rz_info, "rz", do_rz, n_rw_threads, n_rz_threads); -} - -static void pr_params(void) -{ - printf("Parameters:\n"); - printf(" duration: %d s\n", duration); - printf(" # of threads: %u\n", n_rw_threads); - printf(" initial # of keys: %zu\n", init_size); - printf(" initial size hint: %zu\n", qht_n_elems); - printf(" auto-resize: %s\n", - qht_mode & QHT_MODE_AUTO_RESIZE ? "on" : "off"); - if (resize_rate) { - printf(" resize_rate: %f%%\n", resize_rate * 100.0); - printf(" resize range: %zu-%zu\n", resize_min, resize_max); - printf(" # resize threads %u\n", n_rz_threads); - } - printf(" update rate: %f%%\n", update_rate * 100.0); - printf(" offset: %ld\n", populate_offset); - printf(" initial key range: %zu\n", init_range); - printf(" lookup range: %lu\n", lookup_range); - printf(" update range: %lu\n", update_range); -} - -static void do_threshold(double rate, uint64_t *threshold) -{ - /* - * For 0 <= rate <= 1, scale to fit in a uint64_t. - * - * Scale by 2**64, with a special case for 1.0. - * The remainder of the possible values are scattered between 0 - * and 0xfffffffffffff800 (nextafter(0x1p64, 0)). - * - * Note that we cannot simply scale by UINT64_MAX, because that - * value is not representable as an IEEE double value. - * - * If we scale by the next largest value, nextafter(0x1p64, 0), - * then the remainder of the possible values are scattered between - * 0 and 0xfffffffffffff000. Which leaves us with a gap between - * the final two inputs that is twice as large as any other. - */ - if (rate == 1.0) { - *threshold = UINT64_MAX; - } else { - *threshold = rate * 0x1p64; - } -} - -static void htable_init(void) -{ - unsigned long n = MAX(init_range, update_range); - uint64_t r = time(NULL); - size_t retries = 0; - size_t i; - - /* avoid allocating memory later by allocating all the keys now */ - keys = g_malloc(sizeof(*keys) * n); - for (i = 0; i < n; i++) { - long val = populate_offset + i; - - keys[i] = precompute_hash ? h(val) : hval(val); - } - - /* some sanity checks */ - g_assert_cmpuint(lookup_range, <=, n); - - /* compute thresholds */ - do_threshold(update_rate, &update_threshold); - do_threshold(resize_rate, &resize_threshold); - - if (resize_rate) { - resize_min = n / 2; - resize_max = n; - assert(resize_min < resize_max); - } else { - n_rz_threads = 0; - } - - /* initialize the hash table */ - qht_init(&ht, is_equal, qht_n_elems, qht_mode); - assert(init_size <= init_range); - - pr_params(); - - fprintf(stderr, "Initialization: populating %zu items...", init_size); - for (i = 0; i < init_size; i++) { - for (;;) { - uint32_t hash; - long *p; - - r = xorshift64star(r); - p = &keys[r & (init_range - 1)]; - hash = hfunc(*p); - if (qht_insert(&ht, p, hash, NULL)) { - break; - } - retries++; - } - } - fprintf(stderr, " populated after %zu retries\n", retries); -} - -static void add_stats(struct thread_stats *s, struct thread_info *info, int n) -{ - int i; - - for (i = 0; i < n; i++) { - struct thread_stats *stats = &info[i].stats; - - s->rd += stats->rd; - s->not_rd += stats->not_rd; - - s->in += stats->in; - s->not_in += stats->not_in; - - s->rm += stats->rm; - s->not_rm += stats->not_rm; - - s->rz += stats->rz; - s->not_rz += stats->not_rz; - } -} - -static void pr_stats(void) -{ - struct thread_stats s = {}; - double tx; - - add_stats(&s, rw_info, n_rw_threads); - add_stats(&s, rz_info, n_rz_threads); - - printf("Results:\n"); - - if (resize_rate) { - printf(" Resizes: %zu (%.2f%% of %zu)\n", - s.rz, (double)s.rz / (s.rz + s.not_rz) * 100, s.rz + s.not_rz); - } - - printf(" Read: %.2f M (%.2f%% of %.2fM)\n", - (double)s.rd / 1e6, - (double)s.rd / (s.rd + s.not_rd) * 100, - (double)(s.rd + s.not_rd) / 1e6); - printf(" Inserted: %.2f M (%.2f%% of %.2fM)\n", - (double)s.in / 1e6, - (double)s.in / (s.in + s.not_in) * 100, - (double)(s.in + s.not_in) / 1e6); - printf(" Removed: %.2f M (%.2f%% of %.2fM)\n", - (double)s.rm / 1e6, - (double)s.rm / (s.rm + s.not_rm) * 100, - (double)(s.rm + s.not_rm) / 1e6); - - tx = (s.rd + s.not_rd + s.in + s.not_in + s.rm + s.not_rm) / 1e6 / duration; - printf(" Throughput: %.2f MT/s\n", tx); - printf(" Throughput/thread: %.2f MT/s/thread\n", tx / n_rw_threads); -} - -static void run_test(void) -{ - int i; - - while (qatomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) { - cpu_relax(); - } - - qatomic_set(&test_start, true); - g_usleep(duration * G_USEC_PER_SEC); - qatomic_set(&test_stop, true); - - for (i = 0; i < n_rw_threads; i++) { - qemu_thread_join(&rw_threads[i]); - } - for (i = 0; i < n_rz_threads; i++) { - qemu_thread_join(&rz_threads[i]); - } -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - - for (;;) { - c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:pr:Rs:S:u:"); - if (c < 0) { - break; - } - switch (c) { - case 'd': - duration = atoi(optarg); - break; - case 'D': - resize_delay = atol(optarg); - break; - case 'g': - init_range = pow2ceil(atol(optarg)); - lookup_range = pow2ceil(atol(optarg)); - update_range = pow2ceil(atol(optarg)); - qht_n_elems = atol(optarg); - init_size = atol(optarg); - break; - case 'h': - usage_complete(argc, argv); - exit(0); - case 'k': - init_size = atol(optarg); - break; - case 'K': - init_range = pow2ceil(atol(optarg)); - break; - case 'l': - lookup_range = pow2ceil(atol(optarg)); - break; - case 'n': - n_rw_threads = atoi(optarg); - break; - case 'N': - n_rz_threads = atoi(optarg); - break; - case 'o': - populate_offset = atol(optarg); - break; - case 'p': - precompute_hash = true; - hfunc = hval; - break; - case 'r': - update_range = pow2ceil(atol(optarg)); - break; - case 'R': - qht_mode |= QHT_MODE_AUTO_RESIZE; - break; - case 's': - qht_n_elems = atol(optarg); - break; - case 'S': - resize_rate = atof(optarg) / 100.0; - if (resize_rate > 1.0) { - resize_rate = 1.0; - } - break; - case 'u': - update_rate = atof(optarg) / 100.0; - if (update_rate > 1.0) { - update_rate = 1.0; - } - break; - } - } -} - -int main(int argc, char *argv[]) -{ - parse_args(argc, argv); - htable_init(); - create_threads(); - run_test(); - pr_stats(); - return 0; -} -- cgit v1.2.3