From e242c810e9349cbc4b0a22637848278fa2ec0ada Mon Sep 17 00:00:00 2001 From: Intel Date: Thu, 20 Dec 2012 00:00:00 +0100 Subject: [PATCH] app: split performance tests Signed-off-by: Intel --- app/test/Makefile | 3 + app/test/commands.c | 8 + app/test/test.h | 3 + app/test/test_hash.c | 929 +++++++++-------------------------- app/test/test_hash_perf.c | 785 +++++++++++++++++++++++++++++ app/test/test_memcpy.c | 213 +------- app/test/test_memcpy_perf.c | 292 +++++++++++ app/test/test_mempool.c | 246 +--------- app/test/test_mempool_perf.c | 334 +++++++++++++ mk/rte.sdkroot.mk | 4 +- mk/rte.sdktest.mk | 28 +- mk/rte.sdktestall.mk | 2 +- 12 files changed, 1719 insertions(+), 1128 deletions(-) create mode 100644 app/test/test_hash_perf.c create mode 100644 app/test/test_memcpy_perf.c create mode 100644 app/test/test_mempool_perf.c diff --git a/app/test/Makefile b/app/test/Makefile index 32d6a4868d..fe9a9d9659 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -56,10 +56,13 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ring.c SRCS-$(CONFIG_RTE_APP_TEST) += test_rwlock.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mbuf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_logs.c SRCS-$(CONFIG_RTE_APP_TEST) += test_memcpy.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_memcpy_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_hash.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_hash_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_lpm.c SRCS-$(CONFIG_RTE_APP_TEST) += test_debug.c SRCS-$(CONFIG_RTE_APP_TEST) += test_errno.c diff --git a/app/test/commands.c b/app/test/commands.c index fd488d5fc4..9ea801b9db 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -125,6 +125,8 @@ static void cmd_autotest_parsed(void *parsed_result, ret |= test_errno(); if (all || !strcmp(res->autotest, "hash_autotest")) ret |= test_hash(); + if (all || !strcmp(res->autotest, "hash_perf_autotest")) + ret |= test_hash_perf(); if (all || !strcmp(res->autotest, "lpm_autotest")) ret |= test_lpm(); if (all || !strcmp(res->autotest, "cpuflags_autotest")) @@ -157,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret |= test_timer(); if (all || !strcmp(res->autotest, "mempool_autotest")) ret |= test_mempool(); + if (all || !strcmp(res->autotest, "mempool_perf_autotest")) + ret |= test_mempool_perf(); + if (all || !strcmp(res->autotest, "memcpy_perf_autotest")) + ret |= test_memcpy_perf(); if (ret == 0) printf("Test OK\n"); @@ -183,6 +189,8 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#" + "mempool_perf_autotest#hash_perf_autotest#" + "memcpy_perf_autotest#" "all_autotests"); cmdline_parse_inst_t cmd_autotest = { diff --git a/app/test/test.h b/app/test/test.h index 08513a4465..4a61a7b81f 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -63,11 +63,14 @@ int test_logs(void); int test_memzone(void); int test_ring(void); int test_mempool(void); +int test_mempool_perf(void); int test_mbuf(void); int test_timer(void); int test_malloc(void); int test_memcpy(void); +int test_memcpy_perf(void); int test_hash(void); +int test_hash_perf(void); int test_lpm(void); int test_debug(void); int test_errno(void); diff --git a/app/test/test_hash.c b/app/test/test_hash.c index b54fc95f96..fe080c8ef4 100644 --- a/app/test/test_hash.c +++ b/app/test/test_hash.c @@ -64,307 +64,6 @@ #ifdef RTE_LIBRTE_HASH -/* Types of hash table performance test that can be performed */ -enum hash_test_t { - ADD_ON_EMPTY, /*< Add keys to empty table */ - DELETE_ON_EMPTY, /*< Attempt to delete keys from empty table */ - LOOKUP_ON_EMPTY, /*< Attempt to find keys in an empty table */ - ADD_UPDATE, /*< Add/update keys in a full table */ - DELETE, /*< Delete keys from a full table */ - LOOKUP /*< Find keys in a full table */ -}; - -/* Function type for hash table operations. */ -typedef int32_t (*hash_operation)(const struct rte_hash *h, const void *key); - -/* Structure to hold parameters used to run a hash table performance test */ -struct tbl_perf_test_params { - enum hash_test_t test_type; - uint32_t num_iterations; - uint32_t entries; - uint32_t bucket_entries; - uint32_t key_len; - rte_hash_function hash_func; - uint32_t hash_func_init_val; -}; - -#define ITERATIONS 10000 -#define LOCAL_FBK_HASH_ENTRIES_MAX (1 << 15) - -/******************************************************************************* - * Hash table performance test configuration section. - */ -struct tbl_perf_test_params tbl_perf_params[] = -{ -/* Small table, add */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_ON_EMPTY, 1024, 1024, 1, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 1, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 1, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 1, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 64, rte_jhash, 0}, -/* Small table, update */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_UPDATE, ITERATIONS, 1024, 1, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 1, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 1, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 1, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 64, rte_jhash, 0}, -/* Small table, lookup */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ LOOKUP, ITERATIONS, 1024, 1, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 1, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 1, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 1, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 64, rte_jhash, 0}, -/* Big table, add */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 16, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 32, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 48, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 64, rte_jhash, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 64, rte_jhash, 0}, -/* Big table, update */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 16, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 32, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 48, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 64, rte_jhash, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 64, rte_jhash, 0}, -/* Big table, lookup */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ LOOKUP, ITERATIONS, 1048576, 1, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 16, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 1, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 32, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 1, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 48, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 1, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 64, rte_jhash, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 64, rte_jhash, 0}, - -/* Small table, add */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_ON_EMPTY, 1024, 1024, 1, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 1, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 1, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 1, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 2, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 4, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 8, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1024, 1024, 16, 64, rte_hash_crc, 0}, -/* Small table, update */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_UPDATE, ITERATIONS, 1024, 1, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 1, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 1, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 1, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 2, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 4, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 8, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1024, 16, 64, rte_hash_crc, 0}, -/* Small table, lookup */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ LOOKUP, ITERATIONS, 1024, 1, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 1, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 1, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 1, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 2, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 4, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 8, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1024, 16, 64, rte_hash_crc, 0}, -/* Big table, add */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 16, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 32, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 48, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 1, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 2, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 4, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 8, 64, rte_hash_crc, 0}, -{ ADD_ON_EMPTY, 1048576, 1048576, 16, 64, rte_hash_crc, 0}, -/* Big table, update */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 16, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 32, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 48, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 1, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 2, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 4, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 8, 64, rte_hash_crc, 0}, -{ ADD_UPDATE, ITERATIONS, 1048576, 16, 64, rte_hash_crc, 0}, -/* Big table, lookup */ -/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ -{ LOOKUP, ITERATIONS, 1048576, 1, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 16, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 1, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 32, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 1, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 48, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 1, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 2, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 4, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 8, 64, rte_hash_crc, 0}, -{ LOOKUP, ITERATIONS, 1048576, 16, 64, rte_hash_crc, 0}, -}; - -/******************************************************************************/ - /******************************************************************************* * Hash function performance test configuration section. Each performance test * will be performed HASHTEST_ITERATIONS times. @@ -372,16 +71,15 @@ struct tbl_perf_test_params tbl_perf_params[] = * The five arrays below control what tests are performed. Every combination * from the array entries is tested. */ -#define HASHTEST_ITERATIONS 1000000 - #ifdef RTE_MACHINE_CPUFLAG_SSE4_2 static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc}; #else static rte_hash_function hashtest_funcs[] = {rte_jhash}; #endif static uint32_t hashtest_initvals[] = {0}; -static uint32_t hashtest_key_lens[] = {2, 4, 5, 6, 7, 8, 10, 11, 15, 16, 21, 31, 32, 33, 63, 64}; +static uint32_t hashtest_key_lens[] = {0, 2, 4, 5, 6, 7, 8, 10, 11, 15, 16, 21, 31, 32, 33, 63, 64}; /******************************************************************************/ +#define LOCAL_FBK_HASH_ENTRIES_MAX (1 << 15) /* * Check condition and return an error if true. Assumes that "handle" is the @@ -490,6 +188,50 @@ static struct rte_hash_parameters ut_params = { .socket_id = 0, }; +/* + * Test a hash function. + */ +static void run_hash_func_test(rte_hash_function f, uint32_t init_val, + uint32_t key_len) +{ + static uint8_t key[RTE_HASH_KEY_LENGTH_MAX]; + unsigned i; + + + for (i = 0; i < key_len; i++) + key[i] = (uint8_t) rte_rand(); + + /* just to be on the safe side */ + if (!f) + return; + + f(key, key_len, init_val); +} + +/* + * Test all hash functions. + */ +static void run_hash_func_tests(void) +{ + unsigned i, j, k; + + for (i = 0; + i < sizeof(hashtest_funcs) / sizeof(rte_hash_function); + i++) { + for (j = 0; + j < sizeof(hashtest_initvals) / sizeof(uint32_t); + j++) { + for (k = 0; + k < sizeof(hashtest_key_lens) / sizeof(uint32_t); + k++) { + run_hash_func_test(hashtest_funcs[i], + hashtest_initvals[j], + hashtest_key_lens[k]); + } + } + } +} + /* * Basic sequence of operations for a single key: * - add @@ -532,14 +274,14 @@ static int test_add_delete(void) /* * Sequence of operations for a single key: - * - delete: miss - * - add - * - lookup: hit - * - add: update - * - lookup: hit (updated data) - * - delete: hit - * - delete: miss - * - lookup: miss + * - delete: miss + * - add + * - lookup: hit + * - add: update + * - lookup: hit (updated data) + * - delete: hit + * - delete: miss + * - lookup: miss */ static int test_add_update_delete(void) { @@ -707,15 +449,15 @@ static int test_five_keys(void) /* * Add keys to the same bucket until bucket full. - * - add 5 keys to the same bucket (hash created with 4 keys per bucket): - * first 4 successful, 5th unsuccessful + * - add 5 keys to the same bucket (hash created with 4 keys per bucket): + * first 4 successful, 5th unsuccessful * - lookup the 5 keys: 4 hits, 1 miss * - add the 5 keys again: 4 OK, one error as bucket is full * - lookup the 5 keys: 4 hits (updated data), 1 miss - * - delete the 5 keys: 5 OK (even if the 5th is not in the table) - * - lookup the 5 keys: 5 misses - * - add the 5th key: OK - * - lookup the 5th key: hit + * - delete the 5 keys: 5 OK (even if the 5th is not in the table) + * - lookup the 5 keys: 5 misses + * - add the 5th key: OK + * - lookup the 5th key: hit */ static int test_full_bucket(void) { @@ -840,285 +582,6 @@ static int test_full_bucket(void) return 0; } -/* - * To help print out name of hash functions. - */ -static const char *get_hash_name(rte_hash_function f) -{ - if (f == rte_jhash) - return "jhash"; - - if (f == rte_hash_crc) - return "rte_hash_crc"; - - return "UnknownHash"; -} - -/* - * Find average of array of numbers. - */ -static double -get_avg(const uint32_t *array, uint32_t size) -{ - double sum = 0; - unsigned i; - for (i = 0; i < size; i++) - sum += array[i]; - return sum / (double)size; -} - -/* - * Do a single performance test, of one type of operation. - * - * @param h - * hash table to run test on - * @param func - * function to call (add, delete or lookup function) - * @param avg_occupancy - * The average number of entries in each bucket of the hash table - * @param invalid_pos_count - * The amount of errors (e.g. due to a full bucket). - * @return - * The average number of ticks per hash function call. A negative number - * signifies failure. - */ -static double -run_single_tbl_perf_test(const struct rte_hash *h, hash_operation func, - const struct tbl_perf_test_params *params, double *avg_occupancy, - uint32_t *invalid_pos_count) -{ - uint64_t begin, end, ticks = 0; - uint8_t *key = NULL; - uint32_t *bucket_occupancies = NULL; - uint32_t num_buckets, i, j; - int32_t pos; - - /* Initialise */ - num_buckets = params->entries / params->bucket_entries; - key = (uint8_t *) rte_zmalloc("hash key", - params->key_len * sizeof(uint8_t), 16); - if (key == NULL) - return -1; - - bucket_occupancies = (uint32_t *) rte_zmalloc("bucket occupancies", - num_buckets * sizeof(uint32_t), 16); - if (bucket_occupancies == NULL) { - rte_free(key); - return -1; - } - - ticks = 0; - *invalid_pos_count = 0; - - for (i = 0; i < params->num_iterations; i++) { - /* Prepare inputs for the current iteration */ - for (j = 0; j < params->key_len; j++) - key[j] = (uint8_t) rte_rand(); - - /* Perform operation, and measure time it takes */ - begin = rte_rdtsc(); - pos = func(h, key); - end = rte_rdtsc(); - ticks += end - begin; - - /* Other work per iteration */ - if (pos < 0) - *invalid_pos_count += 1; - else - bucket_occupancies[pos / params->bucket_entries]++; - } - *avg_occupancy = get_avg(bucket_occupancies, num_buckets); - - rte_free(bucket_occupancies); - rte_free(key); - - return (double)ticks / params->num_iterations; -} - -/* - * To help print out what tests are being done. - */ -static const char * -get_tbl_perf_test_desc(enum hash_test_t type) -{ - switch (type){ - case ADD_ON_EMPTY: return "Add on Empty"; - case DELETE_ON_EMPTY: return "Delete on Empty"; - case LOOKUP_ON_EMPTY: return "Lookup on Empty"; - case ADD_UPDATE: return "Add Update"; - case DELETE: return "Delete"; - case LOOKUP: return "Lookup"; - default: return "UNKNOWN"; - } -} - -/* - * Run a hash table performance test based on params. - */ -static int -run_tbl_perf_test(struct tbl_perf_test_params *params) -{ - static unsigned calledCount = 5; - struct rte_hash_parameters hash_params = { - .entries = params->entries, - .bucket_entries = params->bucket_entries, - .key_len = params->key_len, - .hash_func = params->hash_func, - .hash_func_init_val = params->hash_func_init_val, - .socket_id = 0, - }; - struct rte_hash *handle; - double avg_occupancy = 0, ticks = 0; - uint32_t num_iterations, invalid_pos; - char name[RTE_HASH_NAMESIZE]; - char hashname[RTE_HASH_NAMESIZE]; - - rte_snprintf(name, 32, "test%u", calledCount++); - hash_params.name = name; - - handle = rte_hash_create(&hash_params); - RETURN_IF_ERROR(handle == NULL, "hash creation failed"); - - switch (params->test_type){ - case ADD_ON_EMPTY: - ticks = run_single_tbl_perf_test(handle, rte_hash_add_key, - params, &avg_occupancy, &invalid_pos); - break; - case DELETE_ON_EMPTY: - ticks = run_single_tbl_perf_test(handle, rte_hash_del_key, - params, &avg_occupancy, &invalid_pos); - break; - case LOOKUP_ON_EMPTY: - ticks = run_single_tbl_perf_test(handle, rte_hash_lookup, - params, &avg_occupancy, &invalid_pos); - break; - case ADD_UPDATE: - num_iterations = params->num_iterations; - params->num_iterations = params->entries; - run_single_tbl_perf_test(handle, rte_hash_add_key, params, - &avg_occupancy, &invalid_pos); - params->num_iterations = num_iterations; - ticks = run_single_tbl_perf_test(handle, rte_hash_add_key, - params, &avg_occupancy, &invalid_pos); - break; - case DELETE: - num_iterations = params->num_iterations; - params->num_iterations = params->entries; - run_single_tbl_perf_test(handle, rte_hash_add_key, params, - &avg_occupancy, &invalid_pos); - - params->num_iterations = num_iterations; - ticks = run_single_tbl_perf_test(handle, rte_hash_del_key, - params, &avg_occupancy, &invalid_pos); - break; - case LOOKUP: - num_iterations = params->num_iterations; - params->num_iterations = params->entries; - run_single_tbl_perf_test(handle, rte_hash_add_key, params, - &avg_occupancy, &invalid_pos); - - params->num_iterations = num_iterations; - ticks = run_single_tbl_perf_test(handle, rte_hash_lookup, - params, &avg_occupancy, &invalid_pos); - break; - default: return -1; - } - - rte_snprintf(hashname, RTE_HASH_NAMESIZE, "%s", get_hash_name(params->hash_func)); - - printf("%-12s, %-15s, %-16u, %-7u, %-18u, %-8u, %-19.2f, %.2f\n", - hashname, - get_tbl_perf_test_desc(params->test_type), - (unsigned) params->key_len, - (unsigned) params->entries, - (unsigned) params->bucket_entries, - (unsigned) invalid_pos, - avg_occupancy, - ticks - ); - - /* Free */ - rte_hash_free(handle); - return 0; -} - -/* - * Run all hash table performance tests. - */ -static int run_all_tbl_perf_tests(void) -{ - unsigned i; - - printf(" *** Hash table performance test results ***\n"); - printf("Hash Func. , Operation , Key size (bytes), Entries, " - "Entries per bucket, Errors , Avg. bucket entries, Ticks/Op.\n"); - - /* Loop through every combination of test parameters */ - for (i = 0; - i < sizeof(tbl_perf_params) / sizeof(struct tbl_perf_test_params); - i++) { - - /* Perform test */ - if (run_tbl_perf_test(&tbl_perf_params[i]) < 0) - return -1; - } - return 0; -} - -/* - * Test a hash function. - */ -static void run_hash_func_test(rte_hash_function f, uint32_t init_val, - uint32_t key_len) -{ - static uint8_t key[RTE_HASH_KEY_LENGTH_MAX]; - uint64_t ticks = 0, start, end; - unsigned i, j; - - for (i = 0; i < HASHTEST_ITERATIONS; i++) { - - for (j = 0; j < key_len; j++) - key[j] = (uint8_t) rte_rand(); - - start = rte_rdtsc(); - f(key, key_len, init_val); - end = rte_rdtsc(); - ticks += end - start; - } - - printf("%-12s, %-18u, %-13u, %.02f\n", get_hash_name(f), (unsigned) key_len, - (unsigned) init_val, (double)ticks / HASHTEST_ITERATIONS); -} - -/* - * Test all hash functions. - */ -static void run_hash_func_tests(void) -{ - unsigned i, j, k; - - printf("\n\n *** Hash function performance test results ***\n"); - printf(" Number of iterations for each test = %d\n", - HASHTEST_ITERATIONS); - printf("Hash Func. , Key Length (bytes), Initial value, Ticks/Op.\n"); - - for (i = 0; - i < sizeof(hashtest_funcs) / sizeof(rte_hash_function); - i++) { - for (j = 0; - j < sizeof(hashtest_initvals) / sizeof(uint32_t); - j++) { - for (k = 0; - k < sizeof(hashtest_key_lens) / sizeof(uint32_t); - k++) { - run_hash_func_test(hashtest_funcs[i], - hashtest_initvals[j], - hashtest_key_lens[k]); - } - } - } -} - /******************************************************************************/ static int fbk_hash_unit_test(void) @@ -1138,40 +601,85 @@ fbk_hash_unit_test(void) }; struct rte_fbk_hash_params invalid_params_2 = { - .name = "invalid_4", + .name = "invalid_2", .entries = 4, .entries_per_bucket = 3, /* Not power of 2 */ .socket_id = 0, }; struct rte_fbk_hash_params invalid_params_3 = { - .name = "invalid_2", + .name = "invalid_3", .entries = 0, /* Entries is 0 */ .entries_per_bucket = 4, .socket_id = 0, }; struct rte_fbk_hash_params invalid_params_4 = { - .name = "invalid_3", + .name = "invalid_4", .entries = LOCAL_FBK_HASH_ENTRIES_MAX, .entries_per_bucket = 0, /* Entries per bucket is 0 */ .socket_id = 0, }; struct rte_fbk_hash_params invalid_params_5 = { - .name = "invalid_4", + .name = "invalid_5", .entries = 4, .entries_per_bucket = 8, /* Entries per bucket > entries */ .socket_id = 0, }; struct rte_fbk_hash_params invalid_params_6 = { - .name = "invalid_5", + .name = "invalid_6", .entries = RTE_FBK_HASH_ENTRIES_MAX * 2, /* Entries > max allowed */ .entries_per_bucket = 4, .socket_id = 0, }; + struct rte_fbk_hash_params invalid_params_7 = { + .name = "invalid_7", + .entries = RTE_FBK_HASH_ENTRIES_MAX, + .entries_per_bucket = RTE_FBK_HASH_ENTRIES_PER_BUCKET_MAX * 2, /* Entries > max allowed */ + .socket_id = 0, + }; + + struct rte_fbk_hash_params invalid_params_8 = { + .name = "invalid_7", + .entries = RTE_FBK_HASH_ENTRIES_MAX, + .entries_per_bucket = 4, + .socket_id = RTE_MAX_NUMA_NODES + 1, /* invalid socket */ + }; + + /* try to create two hashes with identical names + * in this case, trying to create a second one will not + * fail but will simply return pointer to the existing + * hash with that name. sort of like a "find hash by name" :-) + */ + struct rte_fbk_hash_params invalid_params_same_name_1 = { + .name = "same_name", /* hash with identical name */ + .entries = 4, + .entries_per_bucket = 2, + .socket_id = 0, + }; + + /* trying to create this hash should return a pointer to an existing hash */ + struct rte_fbk_hash_params invalid_params_same_name_2 = { + .name = "same_name", /* hash with identical name */ + .entries = RTE_FBK_HASH_ENTRIES_MAX, + .entries_per_bucket = 4, + .socket_id = 0, + }; + + /* this is a sanity check for "same name" test + * creating this hash will check if we are actually able to create + * multiple hashes with different names (instead of having just one). + */ + struct rte_fbk_hash_params different_name = { + .name = "different_name", /* different name */ + .entries = RTE_FBK_HASH_ENTRIES_MAX, + .entries_per_bucket = 4, + .socket_id = 0, + }; + struct rte_fbk_hash_params params_jhash = { .name = "valid", .entries = LOCAL_FBK_HASH_ENTRIES_MAX, @@ -1186,11 +694,11 @@ fbk_hash_unit_test(void) .entries = LOCAL_FBK_HASH_ENTRIES_MAX, .entries_per_bucket = 4, .socket_id = 0, - .hash_func = 0, /* Tests for null hash_func */ + .hash_func = NULL, /* Tests for null hash_func */ .init_val = RTE_FBK_HASH_INIT_VAL_DEFAULT, }; - struct rte_fbk_hash_table *handle; + struct rte_fbk_hash_table *handle, *tmp; uint32_t keys[5] = {0xc6e18639, 0xe67c201c, 0xd4c8cffd, 0x44728691, 0xd5430fa9}; uint16_t vals[5] = {28108, 5699, 38490, 2166, 61571}; @@ -1217,6 +725,35 @@ fbk_hash_unit_test(void) handle = rte_fbk_hash_create(&invalid_params_6); RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed"); + handle = rte_fbk_hash_create(&invalid_params_7); + RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed"); + + handle = rte_fbk_hash_create(&invalid_params_8); + RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed"); + + handle = rte_fbk_hash_create(&invalid_params_same_name_1); + RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation should have succeeded"); + + tmp = rte_fbk_hash_create(&invalid_params_same_name_2); + RETURN_IF_ERROR_FBK(tmp == NULL, "fbk hash creation should have succeeded"); + if (tmp != handle) { + printf("ERROR line %d: hashes should have been the same\n", __LINE__); + rte_fbk_hash_free(handle); + rte_fbk_hash_free(tmp); + return -1; + } + + /* we are not freeing tmp or handle here because we need a hash list + * to be not empty for the next test */ + + /* create a hash in non-empty list - good for coverage */ + tmp = rte_fbk_hash_create(&different_name); + RETURN_IF_ERROR_FBK(tmp == NULL, "fbk hash creation should have succeeded"); + + /* free both hashes */ + rte_fbk_hash_free(handle); + rte_fbk_hash_free(tmp); + /* Create empty jhash hash. */ handle = rte_fbk_hash_create(¶ms_jhash); RETURN_IF_ERROR_FBK(handle == NULL, "fbk jhash hash creation failed"); @@ -1306,86 +843,36 @@ fbk_hash_unit_test(void) "fbk hash lookup should have failed"); } - /* Cleanup. */ - rte_fbk_hash_free(handle); + /* coverage */ - /* Cover the NULL case. */ - rte_fbk_hash_free(0); + /* fill up the hash_table */ + for (i = 0; i < RTE_FBK_HASH_ENTRIES_MAX + 1; i++) + rte_fbk_hash_add_key(handle, i, (uint16_t) i); - return 0; -} + /* Find non-existent key in a full hashtable */ + status = rte_fbk_hash_lookup(handle, RTE_FBK_HASH_ENTRIES_MAX + 1); + RETURN_IF_ERROR_FBK(status != -ENOENT, + "fbk hash lookup succeeded"); -/* Control operation of performance testing of fbk hash. */ -#define LOAD_FACTOR 0.667 /* How full to make the hash table. */ -#define TEST_SIZE 1000000 /* How many operations to time. */ -#define TEST_ITERATIONS 30 /* How many measurements to take. */ -#define ENTRIES (1 << 15) /* How many entries. */ + /* Delete non-existent key in a full hashtable */ + status = rte_fbk_hash_delete_key(handle, RTE_FBK_HASH_ENTRIES_MAX + 1); + RETURN_IF_ERROR_FBK(status != -ENOENT, + "fbk hash delete succeeded"); -static int -fbk_hash_perf_test(void) -{ - struct rte_fbk_hash_params params = { - .name = "fbk_hash_test", - .entries = ENTRIES, - .entries_per_bucket = 4, - .socket_id = 0, - }; - struct rte_fbk_hash_table *handle; - uint32_t keys[ENTRIES] = {0}; - unsigned indexes[TEST_SIZE]; - uint64_t lookup_time = 0; - unsigned added = 0; - unsigned value = 0; - unsigned i, j; - - handle = rte_fbk_hash_create(¶ms); - RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation failed"); - - /* Generate random keys and values. */ - for (i = 0; i < ENTRIES; i++) { - uint32_t key = (uint32_t)rte_rand(); - key = ((uint64_t)key << 32) | (uint64_t)rte_rand(); - uint16_t val = (uint16_t)rte_rand(); - - if (rte_fbk_hash_add_key(handle, key, val) == 0) { - keys[added] = key; - added++; - } - if (added > (LOAD_FACTOR * ENTRIES)) { - break; - } - } + /* Delete one key from a full hashtable */ + status = rte_fbk_hash_delete_key(handle, 1); + RETURN_IF_ERROR_FBK(status != 0, + "fbk hash delete failed"); - for (i = 0; i < TEST_ITERATIONS; i++) { - uint64_t begin; - uint64_t end; - - /* Generate random indexes into keys[] array. */ - for (j = 0; j < TEST_SIZE; j++) { - indexes[j] = rte_rand() % added; - } - - begin = rte_rdtsc(); - /* Do lookups */ - for (j = 0; j < TEST_SIZE; j++) { - value += rte_fbk_hash_lookup(handle, keys[indexes[j]]); - } - end = rte_rdtsc(); - lookup_time += (double)(end - begin); - } - - printf("\n\n *** FBK Hash function performance test results ***\n"); - /* - * The use of the 'value' variable ensures that the hash lookup is not - * being optimised out by the compiler. - */ - if (value != 0) - printf("Number of ticks per lookup = %g\n", - (double)lookup_time / - ((double)TEST_ITERATIONS * (double)TEST_SIZE)); + /* Clear all entries. */ + rte_fbk_hash_clear_all(handle); + /* Cleanup. */ rte_fbk_hash_free(handle); + /* Cover the NULL case. */ + rte_fbk_hash_free(0); + return 0; } @@ -1506,10 +993,82 @@ static int test_hash_creation_with_bad_parameters(void) handle = rte_hash_create(¶ms); if (handle != NULL) { rte_hash_free(handle); - printf("Impossible creating hash sucessfully if key_len is greater than the maximun\n"); + printf("Impossible creating hash sucessfully if key_len is greater than the maximum\n"); + return -1; + } + + memcpy(¶ms, &ut_params, sizeof(params)); + params.name = "creation_with_bad_parameters_7"; + params.socket_id = RTE_MAX_NUMA_NODES + 1; + handle = rte_hash_create(¶ms); + if (handle != NULL) { + rte_hash_free(handle); + printf("Impossible creating hash sucessfully with invalid socket\n"); + return -1; + } + + rte_hash_free(handle); + + return 0; +} + +/* + * Do tests for hash creation with parameters that look incorrect + * but are actually valid. + */ +static int +test_hash_creation_with_good_parameters(void) +{ + struct rte_hash *handle, *tmp; + struct rte_hash_parameters params; + + /* create with null hash function - should choose DEFAULT_HASH_FUNC */ + memcpy(¶ms, &ut_params, sizeof(params)); + params.name = "same_name"; + params.hash_func = NULL; + handle = rte_hash_create(¶ms); + if (handle == NULL) { + printf("Creating hash with null hash_func failed\n"); + return -1; + } + if (handle->hash_func == NULL) { + printf("Hash function should have been DEFAULT_HASH_FUNC\n"); + return -1; + } + + /* this test is trying to create a hash with the same name as previous one. + * this should return a pointer to the hash we previously created. + * the previous hash isn't freed exactly for the purpose of it being in + * the hash list. + */ + memcpy(¶ms, &ut_params, sizeof(params)); + params.name = "same_name"; + tmp = rte_hash_create(¶ms); + + /* check if the returned handle is actually equal to the previous hash */ + if (handle != tmp) { + rte_hash_free(handle); + rte_hash_free(tmp); + printf("Creating hash with existing name was successful\n"); return -1; } + /* try creating hash when there already are hashes in the list. + * the previous hash is not freed to have a non-empty hash list. + * the other hash that's in the list is still pointed to by "handle" var. + */ + memcpy(¶ms, &ut_params, sizeof(params)); + params.name = "different_name"; + tmp = rte_hash_create(¶ms); + if (tmp == NULL) { + rte_hash_free(handle); + printf("Creating hash with valid parameters failed\n"); + return -1; + } + + rte_hash_free(tmp); + rte_hash_free(handle); + return 0; } @@ -1606,7 +1165,7 @@ test_hash_jhash_1word(const void *key, uint32_t length, uint32_t initval) { const uint32_t *k = key; - length =length; + RTE_SET_USED(length); return rte_jhash_1word(k[0], initval); } @@ -1616,7 +1175,7 @@ test_hash_jhash_2word(const void *key, uint32_t length, uint32_t initval) { const uint32_t *k = key; - length =length; + RTE_SET_USED(length); return rte_jhash_2words(k[0], k[1], initval); } @@ -1626,7 +1185,7 @@ test_hash_jhash_3word(const void *key, uint32_t length, uint32_t initval) { const uint32_t *k = key; - length =length; + RTE_SET_USED(length); return rte_jhash_3words(k[0], k[1], k[2], initval); } @@ -1761,18 +1320,18 @@ int test_hash(void) return -1; if (test_full_bucket() < 0) return -1; - if (run_all_tbl_perf_tests() < 0) - return -1; - run_hash_func_tests(); if (test_fbk_hash_find_existing() < 0) return -1; if (fbk_hash_unit_test() < 0) return -1; - if (fbk_hash_perf_test() < 0) - return -1; if (test_hash_creation_with_bad_parameters() < 0) return -1; + if (test_hash_creation_with_good_parameters() < 0) + return -1; + + run_hash_func_tests(); + return 0; } #else diff --git a/app/test/test_hash_perf.c b/app/test/test_hash_perf.c new file mode 100644 index 0000000000..0832ab89f5 --- /dev/null +++ b/app/test/test_hash_perf.c @@ -0,0 +1,785 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef RTE_MACHINE_CPUFLAG_SSE4_2 +#include +#endif +#include + +#include "test.h" + +#ifdef RTE_LIBRTE_HASH + +/* Types of hash table performance test that can be performed */ +enum hash_test_t { + ADD_ON_EMPTY, /*< Add keys to empty table */ + DELETE_ON_EMPTY, /*< Attempt to delete keys from empty table */ + LOOKUP_ON_EMPTY, /*< Attempt to find keys in an empty table */ + ADD_UPDATE, /*< Add/update keys in a full table */ + DELETE, /*< Delete keys from a full table */ + LOOKUP /*< Find keys in a full table */ +}; + +/* Function type for hash table operations. */ +typedef int32_t (*hash_operation)(const struct rte_hash *h, const void *key); + +/* Structure to hold parameters used to run a hash table performance test */ +struct tbl_perf_test_params { + enum hash_test_t test_type; + uint32_t num_iterations; + uint32_t entries; + uint32_t bucket_entries; + uint32_t key_len; + rte_hash_function hash_func; + uint32_t hash_func_init_val; +}; + +#define ITERATIONS 10000 +#define LOCAL_FBK_HASH_ENTRIES_MAX (1 << 15) + +/******************************************************************************* + * Hash function performance test configuration section. Each performance test + * will be performed HASHTEST_ITERATIONS times. + * + * The five arrays below control what tests are performed. Every combination + * from the array entries is tested. + */ +#define HASHTEST_ITERATIONS 1000000 + +#ifdef RTE_MACHINE_CPUFLAG_SSE4_2 +static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc}; +#else +static rte_hash_function hashtest_funcs[] = {rte_jhash}; +#endif +static uint32_t hashtest_initvals[] = {0}; +static uint32_t hashtest_key_lens[] = {2, 4, 5, 6, 7, 8, 10, 11, 15, 16, 21, 31, 32, 33, 63, 64}; +/******************************************************************************/ + +/******************************************************************************* + * Hash table performance test configuration section. + */ +struct tbl_perf_test_params tbl_perf_params[] = +{ +/* Small table, add */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_ON_EMPTY, 1024, 1024, 1, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 1, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 1, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 1, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 64, rte_jhash, 0}, +/* Small table, update */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_UPDATE, ITERATIONS, 1024, 1, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 1, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 1, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 1, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 64, rte_jhash, 0}, +/* Small table, lookup */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ LOOKUP, ITERATIONS, 1024, 1, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 1, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 1, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 1, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 64, rte_jhash, 0}, +/* Big table, add */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 16, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 32, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 48, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 64, rte_jhash, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 64, rte_jhash, 0}, +/* Big table, update */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 16, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 32, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 48, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 64, rte_jhash, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 64, rte_jhash, 0}, +/* Big table, lookup */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ LOOKUP, ITERATIONS, 1048576, 1, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 16, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 1, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 32, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 1, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 48, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 1, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 64, rte_jhash, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 64, rte_jhash, 0}, +#ifdef RTE_MACHINE_CPUFLAG_SSE4_2 +/* Small table, add */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_ON_EMPTY, 1024, 1024, 1, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 1, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 1, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 1, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 2, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 4, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 8, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1024, 1024, 16, 64, rte_hash_crc, 0}, +/* Small table, update */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_UPDATE, ITERATIONS, 1024, 1, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 1, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 1, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 1, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 2, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 4, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 8, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1024, 16, 64, rte_hash_crc, 0}, +/* Small table, lookup */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ LOOKUP, ITERATIONS, 1024, 1, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 1, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 1, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 1, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 2, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 4, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 8, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1024, 16, 64, rte_hash_crc, 0}, +/* Big table, add */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 16, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 32, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 48, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 1, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 2, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 4, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 8, 64, rte_hash_crc, 0}, +{ ADD_ON_EMPTY, 1048576, 1048576, 16, 64, rte_hash_crc, 0}, +/* Big table, update */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 16, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 32, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 48, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 1, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 2, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 4, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 8, 64, rte_hash_crc, 0}, +{ ADD_UPDATE, ITERATIONS, 1048576, 16, 64, rte_hash_crc, 0}, +/* Big table, lookup */ +/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */ +{ LOOKUP, ITERATIONS, 1048576, 1, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 16, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 1, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 32, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 1, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 48, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 1, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 2, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 4, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 8, 64, rte_hash_crc, 0}, +{ LOOKUP, ITERATIONS, 1048576, 16, 64, rte_hash_crc, 0}, +#endif +}; + +/******************************************************************************/ + +/* + * Check condition and return an error if true. Assumes that "handle" is the + * name of the hash structure pointer to be freed. + */ +#define RETURN_IF_ERROR(cond, str, ...) do { \ + if (cond) { \ + printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \ + if (handle) rte_hash_free(handle); \ + return -1; \ + } \ +} while(0) + +#define RETURN_IF_ERROR_FBK(cond, str, ...) do { \ + if (cond) { \ + printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \ + if (handle) rte_fbk_hash_free(handle); \ + return -1; \ + } \ +} while(0) + +/* + * Find average of array of numbers. + */ +static double +get_avg(const uint32_t *array, uint32_t size) +{ + double sum = 0; + unsigned i; + for (i = 0; i < size; i++) + sum += array[i]; + return sum / (double)size; +} + +/* + * To help print out name of hash functions. + */ +static const char *get_hash_name(rte_hash_function f) +{ + if (f == rte_jhash) + return "jhash"; + +#ifdef RTE_MACHINE_CPUFLAG_SSE4_2 + if (f == rte_hash_crc) + return "rte_hash_crc"; +#endif + + return "UnknownHash"; +} + +/* + * Do a single performance test, of one type of operation. + * + * @param h + * hash table to run test on + * @param func + * function to call (add, delete or lookup function) + * @param avg_occupancy + * The average number of entries in each bucket of the hash table + * @param invalid_pos_count + * The amount of errors (e.g. due to a full bucket). + * @return + * The average number of ticks per hash function call. A negative number + * signifies failure. + */ +static double +run_single_tbl_perf_test(const struct rte_hash *h, hash_operation func, + const struct tbl_perf_test_params *params, double *avg_occupancy, + uint32_t *invalid_pos_count) +{ + uint64_t begin, end, ticks = 0; + uint8_t *key = NULL; + uint32_t *bucket_occupancies = NULL; + uint32_t num_buckets, i, j; + int32_t pos; + + /* Initialise */ + num_buckets = params->entries / params->bucket_entries; + key = (uint8_t *) rte_zmalloc("hash key", + params->key_len * sizeof(uint8_t), 16); + if (key == NULL) + return -1; + + bucket_occupancies = (uint32_t *) rte_zmalloc("bucket occupancies", + num_buckets * sizeof(uint32_t), 16); + if (bucket_occupancies == NULL) { + rte_free(key); + return -1; + } + + ticks = 0; + *invalid_pos_count = 0; + + for (i = 0; i < params->num_iterations; i++) { + /* Prepare inputs for the current iteration */ + for (j = 0; j < params->key_len; j++) + key[j] = (uint8_t) rte_rand(); + + /* Perform operation, and measure time it takes */ + begin = rte_rdtsc(); + pos = func(h, key); + end = rte_rdtsc(); + ticks += end - begin; + + /* Other work per iteration */ + if (pos < 0) + *invalid_pos_count += 1; + else + bucket_occupancies[pos / params->bucket_entries]++; + } + *avg_occupancy = get_avg(bucket_occupancies, num_buckets); + + rte_free(bucket_occupancies); + rte_free(key); + + return (double)ticks / params->num_iterations; +} + +/* + * To help print out what tests are being done. + */ +static const char * +get_tbl_perf_test_desc(enum hash_test_t type) +{ + switch (type){ + case ADD_ON_EMPTY: return "Add on Empty"; + case DELETE_ON_EMPTY: return "Delete on Empty"; + case LOOKUP_ON_EMPTY: return "Lookup on Empty"; + case ADD_UPDATE: return "Add Update"; + case DELETE: return "Delete"; + case LOOKUP: return "Lookup"; + default: return "UNKNOWN"; + } +} + +/* + * Run a hash table performance test based on params. + */ +static int +run_tbl_perf_test(struct tbl_perf_test_params *params) +{ + static unsigned calledCount = 5; + struct rte_hash_parameters hash_params = { + .entries = params->entries, + .bucket_entries = params->bucket_entries, + .key_len = params->key_len, + .hash_func = params->hash_func, + .hash_func_init_val = params->hash_func_init_val, + .socket_id = rte_socket_id(), + }; + struct rte_hash *handle; + double avg_occupancy = 0, ticks = 0; + uint32_t num_iterations, invalid_pos; + char name[RTE_HASH_NAMESIZE]; + char hashname[RTE_HASH_NAMESIZE]; + + rte_snprintf(name, 32, "test%u", calledCount++); + hash_params.name = name; + + handle = rte_hash_create(&hash_params); + RETURN_IF_ERROR(handle == NULL, "hash creation failed"); + + switch (params->test_type){ + case ADD_ON_EMPTY: + ticks = run_single_tbl_perf_test(handle, rte_hash_add_key, + params, &avg_occupancy, &invalid_pos); + break; + case DELETE_ON_EMPTY: + ticks = run_single_tbl_perf_test(handle, rte_hash_del_key, + params, &avg_occupancy, &invalid_pos); + break; + case LOOKUP_ON_EMPTY: + ticks = run_single_tbl_perf_test(handle, rte_hash_lookup, + params, &avg_occupancy, &invalid_pos); + break; + case ADD_UPDATE: + num_iterations = params->num_iterations; + params->num_iterations = params->entries; + run_single_tbl_perf_test(handle, rte_hash_add_key, params, + &avg_occupancy, &invalid_pos); + params->num_iterations = num_iterations; + ticks = run_single_tbl_perf_test(handle, rte_hash_add_key, + params, &avg_occupancy, &invalid_pos); + break; + case DELETE: + num_iterations = params->num_iterations; + params->num_iterations = params->entries; + run_single_tbl_perf_test(handle, rte_hash_add_key, params, + &avg_occupancy, &invalid_pos); + + params->num_iterations = num_iterations; + ticks = run_single_tbl_perf_test(handle, rte_hash_del_key, + params, &avg_occupancy, &invalid_pos); + break; + case LOOKUP: + num_iterations = params->num_iterations; + params->num_iterations = params->entries; + run_single_tbl_perf_test(handle, rte_hash_add_key, params, + &avg_occupancy, &invalid_pos); + + params->num_iterations = num_iterations; + ticks = run_single_tbl_perf_test(handle, rte_hash_lookup, + params, &avg_occupancy, &invalid_pos); + break; + default: return -1; + } + + rte_snprintf(hashname, RTE_HASH_NAMESIZE, "%s", get_hash_name(params->hash_func)); + + printf("%-12s, %-15s, %-16u, %-7u, %-18u, %-8u, %-19.2f, %.2f\n", + hashname, + get_tbl_perf_test_desc(params->test_type), + (unsigned) params->key_len, + (unsigned) params->entries, + (unsigned) params->bucket_entries, + (unsigned) invalid_pos, + avg_occupancy, + ticks + ); + + /* Free */ + rte_hash_free(handle); + return 0; +} + +/* + * Run all hash table performance tests. + */ +static int run_all_tbl_perf_tests(void) +{ + unsigned i; + + printf(" *** Hash table performance test results ***\n"); + printf("Hash Func. , Operation , Key size (bytes), Entries, " + "Entries per bucket, Errors , Avg. bucket entries, Ticks/Op.\n"); + + /* Loop through every combination of test parameters */ + for (i = 0; + i < sizeof(tbl_perf_params) / sizeof(struct tbl_perf_test_params); + i++) { + + /* Perform test */ + if (run_tbl_perf_test(&tbl_perf_params[i]) < 0) + return -1; + } + return 0; +} + +/* + * Test a hash function. + */ +static void run_hash_func_test(rte_hash_function f, uint32_t init_val, + uint32_t key_len) +{ + static uint8_t key[RTE_HASH_KEY_LENGTH_MAX]; + uint64_t ticks = 0, start, end; + unsigned i, j; + + for (i = 0; i < HASHTEST_ITERATIONS; i++) { + + for (j = 0; j < key_len; j++) + key[j] = (uint8_t) rte_rand(); + + start = rte_rdtsc(); + f(key, key_len, init_val); + end = rte_rdtsc(); + ticks += end - start; + } + + printf("%-12s, %-18u, %-13u, %.02f\n", get_hash_name(f), (unsigned) key_len, + (unsigned) init_val, (double)ticks / HASHTEST_ITERATIONS); +} + +/* + * Test all hash functions. + */ +static void run_hash_func_tests(void) +{ + unsigned i, j, k; + + printf("\n\n *** Hash function performance test results ***\n"); + printf(" Number of iterations for each test = %d\n", + HASHTEST_ITERATIONS); + printf("Hash Func. , Key Length (bytes), Initial value, Ticks/Op.\n"); + + for (i = 0; + i < sizeof(hashtest_funcs) / sizeof(rte_hash_function); + i++) { + for (j = 0; + j < sizeof(hashtest_initvals) / sizeof(uint32_t); + j++) { + for (k = 0; + k < sizeof(hashtest_key_lens) / sizeof(uint32_t); + k++) { + run_hash_func_test(hashtest_funcs[i], + hashtest_initvals[j], + hashtest_key_lens[k]); + } + } + } +} + +/* Control operation of performance testing of fbk hash. */ +#define LOAD_FACTOR 0.667 /* How full to make the hash table. */ +#define TEST_SIZE 1000000 /* How many operations to time. */ +#define TEST_ITERATIONS 30 /* How many measurements to take. */ +#define ENTRIES (1 << 15) /* How many entries. */ + +static int +fbk_hash_perf_test(void) +{ + struct rte_fbk_hash_params params = { + .name = "fbk_hash_test", + .entries = ENTRIES, + .entries_per_bucket = 4, + .socket_id = rte_socket_id(), + }; + struct rte_fbk_hash_table *handle; + uint32_t keys[ENTRIES] = {0}; + unsigned indexes[TEST_SIZE]; + uint64_t lookup_time = 0; + unsigned added = 0; + unsigned value = 0; + unsigned i, j; + + handle = rte_fbk_hash_create(¶ms); + RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation failed"); + + /* Generate random keys and values. */ + for (i = 0; i < ENTRIES; i++) { + uint32_t key = (uint32_t)rte_rand(); + key = ((uint64_t)key << 32) | (uint64_t)rte_rand(); + uint16_t val = (uint16_t)rte_rand(); + + if (rte_fbk_hash_add_key(handle, key, val) == 0) { + keys[added] = key; + added++; + } + if (added > (LOAD_FACTOR * ENTRIES)) { + break; + } + } + + for (i = 0; i < TEST_ITERATIONS; i++) { + uint64_t begin; + uint64_t end; + + /* Generate random indexes into keys[] array. */ + for (j = 0; j < TEST_SIZE; j++) { + indexes[j] = rte_rand() % added; + } + + begin = rte_rdtsc(); + /* Do lookups */ + for (j = 0; j < TEST_SIZE; j++) { + value += rte_fbk_hash_lookup(handle, keys[indexes[j]]); + } + end = rte_rdtsc(); + lookup_time += (double)(end - begin); + } + + printf("\n\n *** FBK Hash function performance test results ***\n"); + /* + * The use of the 'value' variable ensures that the hash lookup is not + * being optimised out by the compiler. + */ + if (value != 0) + printf("Number of ticks per lookup = %g\n", + (double)lookup_time / + ((double)TEST_ITERATIONS * (double)TEST_SIZE)); + + rte_fbk_hash_free(handle); + + return 0; +} + +/* + * Do all unit and performance tests. + */ +int test_hash_perf(void) +{ + if (run_all_tbl_perf_tests() < 0) + return -1; + run_hash_func_tests(); + + if (fbk_hash_perf_test() < 0) + return -1; + return 0; +} +#else + +int +test_hash_perf(void) +{ + printf("The Hash library is not included in this build\n"); + return 0; +} + +#endif diff --git a/app/test/test_memcpy.c b/app/test/test_memcpy.c index 22c25c4ada..f20aff6288 100644 --- a/app/test/test_memcpy.c +++ b/app/test/test_memcpy.c @@ -82,210 +82,7 @@ static size_t buf_sizes[TEST_VALUE_RANGE]; /* Data is aligned on this many bytes (power of 2) */ #define ALIGNMENT_UNIT 16 -/* - * Pointers used in performance tests. The two large buffers are for uncached - * access where random addresses within the buffer are used for each - * memcpy. The two small buffers are for cached access. - */ -static uint8_t *large_buf_read, *large_buf_write, - *small_buf_read, *small_buf_write; - -/* Initialise data buffers. */ -static int -init_buffers(void) -{ - unsigned i; - - large_buf_read = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT); - if (large_buf_read == NULL) - goto error_large_buf_read; - - large_buf_write = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT); - if (large_buf_write == NULL) - goto error_large_buf_write; - - small_buf_read = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT); - if (small_buf_read == NULL) - goto error_small_buf_read; - - small_buf_write = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT); - if (small_buf_write == NULL) - goto error_small_buf_write; - - for (i = 0; i < LARGE_BUFFER_SIZE; i++) - large_buf_read[i] = rte_rand(); - for (i = 0; i < SMALL_BUFFER_SIZE; i++) - small_buf_read[i] = rte_rand(); - - return 0; - -error_small_buf_write: - rte_free(small_buf_read); -error_small_buf_read: - rte_free(large_buf_write); -error_large_buf_write: - rte_free(large_buf_read); -error_large_buf_read: - printf("ERROR: not enough memory"); - return -1; -} - -/* Cleanup data buffers */ -static void -free_buffers(void) -{ - rte_free(large_buf_read); - rte_free(large_buf_write); - rte_free(small_buf_read); - rte_free(small_buf_write); -} - -/* - * Get a random offset into large array, with enough space needed to perform - * max copy size. Offset is aligned. - */ -static inline size_t -get_rand_offset(void) -{ - return ((rte_rand() % (LARGE_BUFFER_SIZE - SMALL_BUFFER_SIZE)) & - ~(ALIGNMENT_UNIT - 1)); -} - -/* Fill in source and destination addresses. */ -static inline void -fill_addr_arrays(size_t *dst_addr, int is_dst_cached, - size_t *src_addr, int is_src_cached) -{ - unsigned int i; - - for (i = 0; i < TEST_BATCH_SIZE; i++) { - dst_addr[i] = (is_dst_cached) ? 0 : get_rand_offset(); - src_addr[i] = (is_src_cached) ? 0 : get_rand_offset(); - } -} -/* Integer division with round to nearest */ -static inline uint64_t -div_round(uint64_t dividend, uint64_t divisor) -{ - return ((2 * dividend) + divisor) / (2 * divisor); -} - -/* - * WORKAROUND: For some reason the first test doing an uncached write - * takes a very long time (~25 times longer than is expected). So we do - * it once without timing. - */ -static void -do_uncached_write(uint8_t *dst, int is_dst_cached, - const uint8_t *src, int is_src_cached, size_t size) -{ - unsigned i, j; - size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE]; - - for (i = 0; i < (TEST_ITERATIONS / TEST_BATCH_SIZE); i++) { - fill_addr_arrays(dst_addrs, is_dst_cached, - src_addrs, is_src_cached); - for (j = 0; j < TEST_BATCH_SIZE; j++) - rte_memcpy(dst+dst_addrs[j], src+src_addrs[j], size); - } -} - -/* - * Run a single memcpy performance test. This is a macro to ensure that if - * the "size" parameter is a constant it won't be converted to a variable. - */ -#define SINGLE_PERF_TEST(dst, is_dst_cached, src, is_src_cached, size) do { \ - unsigned int iter, t; \ - size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE]; \ - uint64_t start_time, total_time = 0; \ - uint64_t total_time2 = 0; \ - for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \ - fill_addr_arrays(dst_addrs, is_dst_cached, \ - src_addrs, is_src_cached); \ - start_time = rte_rdtsc(); \ - for (t = 0; t < TEST_BATCH_SIZE; t++) \ - rte_memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \ - total_time += rte_rdtsc() - start_time; \ - } \ - for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \ - fill_addr_arrays(dst_addrs, is_dst_cached, \ - src_addrs, is_src_cached); \ - start_time = rte_rdtsc(); \ - for (t = 0; t < TEST_BATCH_SIZE; t++) \ - memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \ - total_time2 += rte_rdtsc() - start_time; \ - } \ - printf("%9u/", (unsigned)div_round(total_time, TEST_ITERATIONS)); \ - printf("%4u", (unsigned)div_round(total_time2, TEST_ITERATIONS)); \ -} while (0) - -/* Run memcpy() tests for each cached/uncached permutation. */ -#define ALL_PERF_TESTS_FOR_SIZE(n) do { \ - if (__builtin_constant_p(n)) \ - printf("\nC%6u ", (unsigned)n); \ - else \ - printf("\n%7u ", (unsigned)n); \ - SINGLE_PERF_TEST(small_buf_write, 1, small_buf_read, 1, n); \ - SINGLE_PERF_TEST(large_buf_write, 0, small_buf_read, 1, n); \ - SINGLE_PERF_TEST(small_buf_write, 1, large_buf_read, 0, n); \ - SINGLE_PERF_TEST(large_buf_write, 0, large_buf_read, 0, n); \ -} while (0) - -/* - * Run performance tests for a number of different sizes and cached/uncached - * permutations. - */ -static int -perf_test(void) -{ - const unsigned num_buf_sizes = sizeof(buf_sizes) / sizeof(buf_sizes[0]); - unsigned i; - int ret; - - ret = init_buffers(); - if (ret != 0) - return ret; - -#if TEST_VALUE_RANGE != 0 - /* Setup buf_sizes array, if required */ - for (i = 0; i < TEST_VALUE_RANGE; i++) - buf_sizes[i] = i; -#endif - - /* See function comment */ - do_uncached_write(large_buf_write, 0, small_buf_read, 1, SMALL_BUFFER_SIZE); - - printf("\n** rte_memcpy()/memcpy performance tests **\n" - "======= ============== ============== ============== ==============\n" - " Size Cache to cache Cache to mem Mem to cache Mem to mem\n" - "(bytes) (ticks) (ticks) (ticks) (ticks)\n" - "------- -------------- -------------- -------------- --------------"); - - /* Do tests where size is a variable */ - for (i = 0; i < num_buf_sizes; i++) { - ALL_PERF_TESTS_FOR_SIZE((size_t)buf_sizes[i]); - } - -#ifdef RTE_MEMCPY_BUILTIN_CONSTANT_P - /* Do tests where size is a compile-time constant */ - ALL_PERF_TESTS_FOR_SIZE(63U); - ALL_PERF_TESTS_FOR_SIZE(64U); - ALL_PERF_TESTS_FOR_SIZE(65U); - ALL_PERF_TESTS_FOR_SIZE(255U); - ALL_PERF_TESTS_FOR_SIZE(256U); - ALL_PERF_TESTS_FOR_SIZE(257U); - ALL_PERF_TESTS_FOR_SIZE(1023U); - ALL_PERF_TESTS_FOR_SIZE(1024U); - ALL_PERF_TESTS_FOR_SIZE(1025U); - ALL_PERF_TESTS_FOR_SIZE(1518U); -#endif - printf("\n======= ============== ============== ============== ==============\n\n"); - - free_buffers(); - - return 0; -} /* Structure with base memcpy func pointer, and number of bytes it copies */ struct base_memcpy_func { @@ -345,6 +142,7 @@ test_single_memcpy(unsigned int off_src, unsigned int off_dst, size_t size) unsigned int i; uint8_t dest[SMALL_BUFFER_SIZE + ALIGNMENT_UNIT]; uint8_t src[SMALL_BUFFER_SIZE + ALIGNMENT_UNIT]; + void * ret; /* Setup buffers */ for (i = 0; i < SMALL_BUFFER_SIZE + ALIGNMENT_UNIT; i++) { @@ -353,7 +151,11 @@ test_single_memcpy(unsigned int off_src, unsigned int off_dst, size_t size) } /* Do the copy */ - rte_memcpy(dest + off_dst, src + off_src, size); + ret = rte_memcpy(dest + off_dst, src + off_src, size); + if (ret != (dest + off_dst)) { + printf("rte_memcpy() returned %p, not %p\n", + ret, dest + off_dst); + } /* Check nothing before offset is affected */ for (i = 0; i < off_dst; i++) { @@ -419,9 +221,6 @@ test_memcpy(void) if (ret != 0) return -1; ret = base_func_test(); - if (ret != 0) - return -1; - ret = perf_test(); if (ret != 0) return -1; return 0; diff --git a/app/test/test_memcpy_perf.c b/app/test/test_memcpy_perf.c new file mode 100644 index 0000000000..15eeb0aa04 --- /dev/null +++ b/app/test/test_memcpy_perf.c @@ -0,0 +1,292 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "test.h" + +/* + * Set this to the maximum buffer size you want to test. If it is 0, then the + * values in the buf_sizes[] array below will be used. + */ +#define TEST_VALUE_RANGE 0 + +/* List of buffer sizes to test */ +#if TEST_VALUE_RANGE == 0 +static size_t buf_sizes[] = { + 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129, 255, + 256, 257, 320, 384, 511, 512, 513, 1023, 1024, 1025, 1518, 1522, 1600, + 2048, 3072, 4096, 5120, 6144, 7168, 8192 +}; +/* MUST be as large as largest packet size above */ +#define SMALL_BUFFER_SIZE 8192 +#else /* TEST_VALUE_RANGE != 0 */ +static size_t buf_sizes[TEST_VALUE_RANGE]; +#define SMALL_BUFFER_SIZE TEST_VALUE_RANGE +#endif /* TEST_VALUE_RANGE == 0 */ + + +/* + * Arrays of this size are used for measuring uncached memory accesses by + * picking a random location within the buffer. Make this smaller if there are + * memory allocation errors. + */ +#define LARGE_BUFFER_SIZE (100 * 1024 * 1024) + +/* How many times to run timing loop for performance tests */ +#define TEST_ITERATIONS 1000000 +#define TEST_BATCH_SIZE 100 + +/* Data is aligned on this many bytes (power of 2) */ +#define ALIGNMENT_UNIT 16 + +/* + * Pointers used in performance tests. The two large buffers are for uncached + * access where random addresses within the buffer are used for each + * memcpy. The two small buffers are for cached access. + */ +static uint8_t *large_buf_read, *large_buf_write; +static uint8_t *small_buf_read, *small_buf_write; + +/* Initialise data buffers. */ +static int +init_buffers(void) +{ + unsigned i; + + large_buf_read = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT); + if (large_buf_read == NULL) + goto error_large_buf_read; + + large_buf_write = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT); + if (large_buf_write == NULL) + goto error_large_buf_write; + + small_buf_read = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT); + if (small_buf_read == NULL) + goto error_small_buf_read; + + small_buf_write = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT); + if (small_buf_write == NULL) + goto error_small_buf_write; + + for (i = 0; i < LARGE_BUFFER_SIZE; i++) + large_buf_read[i] = rte_rand(); + for (i = 0; i < SMALL_BUFFER_SIZE; i++) + small_buf_read[i] = rte_rand(); + + return 0; + +error_small_buf_write: + rte_free(small_buf_read); +error_small_buf_read: + rte_free(large_buf_write); +error_large_buf_write: + rte_free(large_buf_read); +error_large_buf_read: + printf("ERROR: not enough memory\n"); + return -1; +} + +/* Cleanup data buffers */ +static void +free_buffers(void) +{ + rte_free(large_buf_read); + rte_free(large_buf_write); + rte_free(small_buf_read); + rte_free(small_buf_write); +} + +/* + * Get a random offset into large array, with enough space needed to perform + * max copy size. Offset is aligned. + */ +static inline size_t +get_rand_offset(void) +{ + return ((rte_rand() % (LARGE_BUFFER_SIZE - SMALL_BUFFER_SIZE)) & + ~(ALIGNMENT_UNIT - 1)); +} + +/* Fill in source and destination addresses. */ +static inline void +fill_addr_arrays(size_t *dst_addr, int is_dst_cached, + size_t *src_addr, int is_src_cached) +{ + unsigned int i; + + for (i = 0; i < TEST_BATCH_SIZE; i++) { + dst_addr[i] = (is_dst_cached) ? 0 : get_rand_offset(); + src_addr[i] = (is_src_cached) ? 0 : get_rand_offset(); + } +} + +/* + * WORKAROUND: For some reason the first test doing an uncached write + * takes a very long time (~25 times longer than is expected). So we do + * it once without timing. + */ +static void +do_uncached_write(uint8_t *dst, int is_dst_cached, + const uint8_t *src, int is_src_cached, size_t size) +{ + unsigned i, j; + size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE]; + + for (i = 0; i < (TEST_ITERATIONS / TEST_BATCH_SIZE); i++) { + fill_addr_arrays(dst_addrs, is_dst_cached, + src_addrs, is_src_cached); + for (j = 0; j < TEST_BATCH_SIZE; j++) + rte_memcpy(dst+dst_addrs[j], src+src_addrs[j], size); + } +} + +/* + * Run a single memcpy performance test. This is a macro to ensure that if + * the "size" parameter is a constant it won't be converted to a variable. + */ +#define SINGLE_PERF_TEST(dst, is_dst_cached, src, is_src_cached, size) do { \ + unsigned int iter, t; \ + size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE]; \ + uint64_t start_time, total_time = 0; \ + uint64_t total_time2 = 0; \ + for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \ + fill_addr_arrays(dst_addrs, is_dst_cached, \ + src_addrs, is_src_cached); \ + start_time = rte_rdtsc(); \ + for (t = 0; t < TEST_BATCH_SIZE; t++) \ + rte_memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \ + total_time += rte_rdtsc() - start_time; \ + } \ + for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \ + fill_addr_arrays(dst_addrs, is_dst_cached, \ + src_addrs, is_src_cached); \ + start_time = rte_rdtsc(); \ + for (t = 0; t < TEST_BATCH_SIZE; t++) \ + memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \ + total_time2 += rte_rdtsc() - start_time; \ + } \ + printf("%8.0f -", (double)total_time /TEST_ITERATIONS); \ + printf("%5.0f", (double)total_time2 / TEST_ITERATIONS); \ +} while (0) + +/* Run memcpy() tests for each cached/uncached permutation. */ +#define ALL_PERF_TESTS_FOR_SIZE(n) do { \ + if (__builtin_constant_p(n)) \ + printf("\nC%6u", (unsigned)n); \ + else \ + printf("\n%7u", (unsigned)n); \ + SINGLE_PERF_TEST(small_buf_write, 1, small_buf_read, 1, n); \ + SINGLE_PERF_TEST(large_buf_write, 0, small_buf_read, 1, n); \ + SINGLE_PERF_TEST(small_buf_write, 1, large_buf_read, 0, n); \ + SINGLE_PERF_TEST(large_buf_write, 0, large_buf_read, 0, n); \ +} while (0) + +/* + * Run performance tests for a number of different sizes and cached/uncached + * permutations. + */ +static int +perf_test(void) +{ + const unsigned num_buf_sizes = sizeof(buf_sizes) / sizeof(buf_sizes[0]); + unsigned i; + int ret; + + ret = init_buffers(); + if (ret != 0) + return ret; + +#if TEST_VALUE_RANGE != 0 + /* Setup buf_sizes array, if required */ + for (i = 0; i < TEST_VALUE_RANGE; i++) + buf_sizes[i] = i; +#endif + + /* See function comment */ + do_uncached_write(large_buf_write, 0, small_buf_read, 1, SMALL_BUFFER_SIZE); + + printf("\n** rte_memcpy() - memcpy perf. tests (C = compile-time constant) **\n" + "======= ============== ============== ============== ==============\n" + " Size Cache to cache Cache to mem Mem to cache Mem to mem\n" + "(bytes) (ticks) (ticks) (ticks) (ticks)\n" + "------- -------------- -------------- -------------- --------------"); + + /* Do tests where size is a variable */ + for (i = 0; i < num_buf_sizes; i++) { + ALL_PERF_TESTS_FOR_SIZE((size_t)buf_sizes[i]); + } + printf("\n------- -------------- -------------- -------------- --------------"); + /* Do tests where size is a compile-time constant */ + ALL_PERF_TESTS_FOR_SIZE(63U); + ALL_PERF_TESTS_FOR_SIZE(64U); + ALL_PERF_TESTS_FOR_SIZE(65U); + ALL_PERF_TESTS_FOR_SIZE(255U); + ALL_PERF_TESTS_FOR_SIZE(256U); + ALL_PERF_TESTS_FOR_SIZE(257U); + ALL_PERF_TESTS_FOR_SIZE(1023U); + ALL_PERF_TESTS_FOR_SIZE(1024U); + ALL_PERF_TESTS_FOR_SIZE(1025U); + ALL_PERF_TESTS_FOR_SIZE(1518U); + + printf("\n======= ============== ============== ============== ==============\n\n"); + + free_buffers(); + + return 0; +} + + +int +test_memcpy_perf(void) +{ + int ret; + + ret = perf_test(); + if (ret != 0) + return -1; + return 0; +} diff --git a/app/test/test_mempool.c b/app/test/test_mempool.c index 11d9389286..cc47232c9e 100644 --- a/app/test/test_mempool.c +++ b/app/test/test_mempool.c @@ -67,40 +67,12 @@ * Mempool * ======= * - * #. Basic tests: done on one core with and without cache: + * Basic tests: done on one core with and without cache: * * - Get one object, put one object * - Get two objects, put two objects * - Get all objects, test that their content is not modified and * put them back in the pool. - * - * #. Performance tests: - * - * Each core get *n_keep* objects per bulk of *n_get_bulk*. Then, - * objects are put back in the pool per bulk of *n_put_bulk*. - * - * This sequence is done during TIME_S seconds. - * - * This test is done on the following configurations: - * - * - Cores configuration (*cores*) - * - * - One core with cache - * - Two cores with cache - * - Max. cores with cache - * - One core without cache - * - Two cores without cache - * - Max. cores without cache - * - * - Bulk size (*n_get_bulk*, *n_put_bulk*) - * - * - Bulk get from 1 to 32 - * - Bulk put from 1 to 32 - * - * - Number of kept objects (*n_keep*) - * - * - 32 - * - 128 */ #define N 65536 @@ -114,163 +86,6 @@ static struct rte_mempool *mp_cache, *mp_nocache; static rte_atomic32_t synchro; -/* number of objects in one bulk operation (get or put) */ -static unsigned n_get_bulk; -static unsigned n_put_bulk; - -/* number of objects retrived from mempool before putting them back */ -static unsigned n_keep; - -/* number of enqueues / dequeues */ -struct mempool_test_stats { - unsigned enq_count; -} __rte_cache_aligned; - -static struct mempool_test_stats stats[RTE_MAX_LCORE]; - -static int -per_lcore_mempool_test(__attribute__((unused)) void *arg) -{ - void *obj_table[MAX_KEEP]; - unsigned i, idx; - unsigned lcore_id = rte_lcore_id(); - int ret; - uint64_t start_cycles, end_cycles; - uint64_t time_diff = 0, hz = rte_get_hpet_hz(); - - /* n_get_bulk and n_put_bulk must be divisors of n_keep */ - if (((n_keep / n_get_bulk) * n_get_bulk) != n_keep) - return -1; - if (((n_keep / n_put_bulk) * n_put_bulk) != n_keep) - return -1; - - stats[lcore_id].enq_count = 0; - - /* wait synchro for slaves */ - if (lcore_id != rte_get_master_lcore()) - while (rte_atomic32_read(&synchro) == 0); - - start_cycles = rte_get_hpet_cycles(); - - while (time_diff/hz < TIME_S) { - for (i = 0; likely(i < (N/n_keep)); i++) { - /* get n_keep objects by bulk of n_bulk */ - idx = 0; - while (idx < n_keep) { - ret = rte_mempool_get_bulk(mp, &obj_table[idx], - n_get_bulk); - if (unlikely(ret < 0)) { - rte_mempool_dump(mp); - rte_ring_dump(mp->ring); - /* in this case, objects are lost... */ - return -1; - } - idx += n_get_bulk; - } - - /* put the objects back */ - idx = 0; - while (idx < n_keep) { - rte_mempool_put_bulk(mp, &obj_table[idx], - n_put_bulk); - idx += n_put_bulk; - } - } - end_cycles = rte_get_hpet_cycles(); - time_diff = end_cycles - start_cycles; - stats[lcore_id].enq_count += N; - } - - return 0; -} - -/* launch all the per-lcore test, and display the result */ -static int -launch_cores(unsigned cores) -{ - unsigned lcore_id; - unsigned rate; - int ret; - unsigned cores_save = cores; - - rte_atomic32_set(&synchro, 0); - - /* reset stats */ - memset(stats, 0, sizeof(stats)); - - printf("mempool_autotest cache=%u cores=%u n_get_bulk=%u " - "n_put_bulk=%u n_keep=%u ", - (unsigned) mp->cache_size, cores, n_get_bulk, n_put_bulk, n_keep); - - if (rte_mempool_count(mp) != MEMPOOL_SIZE) { - printf("mempool is not full\n"); - return -1; - } - - RTE_LCORE_FOREACH_SLAVE(lcore_id) { - if (cores == 1) - break; - cores--; - rte_eal_remote_launch(per_lcore_mempool_test, - NULL, lcore_id); - } - - /* start synchro and launch test on master */ - rte_atomic32_set(&synchro, 1); - - ret = per_lcore_mempool_test(NULL); - - cores = cores_save; - RTE_LCORE_FOREACH_SLAVE(lcore_id) { - if (cores == 1) - break; - cores--; - if (rte_eal_wait_lcore(lcore_id) < 0) - ret = -1; - } - - if (ret < 0) { - printf("per-lcore test returned -1\n"); - return -1; - } - - rate = 0; - for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) - rate += (stats[lcore_id].enq_count / TIME_S); - - printf("rate_persec=%u\n", rate); - - return 0; -} - -/* for a given number of core, launch all test cases */ -static int -do_one_mempool_test(unsigned cores) -{ - unsigned bulk_tab_get[] = { 1, 4, 32, 0 }; - unsigned bulk_tab_put[] = { 1, 4, 32, 0 }; - unsigned keep_tab[] = { 32, 128, 0 }; - unsigned *get_bulk_ptr; - unsigned *put_bulk_ptr; - unsigned *keep_ptr; - int ret; - - for (get_bulk_ptr = bulk_tab_get; *get_bulk_ptr; get_bulk_ptr++) { - for (put_bulk_ptr = bulk_tab_put; *put_bulk_ptr; put_bulk_ptr++) { - for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) { - - n_get_bulk = *get_bulk_ptr; - n_put_bulk = *put_bulk_ptr; - n_keep = *keep_ptr; - ret = launch_cores(cores); - - if (ret < 0) - return -1; - } - } - } - return 0; -} /* @@ -296,31 +111,29 @@ test_mempool_basic(void) char *obj_data; int ret = 0; unsigned i, j; - unsigned old_bulk_count; /* dump the mempool status */ rte_mempool_dump(mp); - old_bulk_count = rte_mempool_get_bulk_count(mp); - rte_mempool_dump(mp); - if (rte_mempool_set_bulk_count(mp, 0) == 0) - return -1; - if (rte_mempool_get_bulk_count(mp) == 0) - return -1; - if (rte_mempool_set_bulk_count(mp, 2) < 0) - return -1; - if (rte_mempool_get_bulk_count(mp) != 2) + + printf("get an object\n"); + if (rte_mempool_get(mp, &obj) < 0) return -1; rte_mempool_dump(mp); - if (rte_mempool_set_bulk_count(mp, old_bulk_count) < 0) + + /* tests that improve coverage */ + printf("get object count\n"); + if (rte_mempool_count(mp) != MEMPOOL_SIZE - 1) return -1; - if (rte_mempool_get_bulk_count(mp) != old_bulk_count) + + printf("get private data\n"); + if (rte_mempool_get_priv(mp) != + (char*) mp + sizeof(struct rte_mempool)) return -1; - rte_mempool_dump(mp); - printf("get an object\n"); - if (rte_mempool_get(mp, &obj) < 0) + printf("get physical address of an object\n"); + if (rte_mempool_virt2phy(mp, obj) != + (phys_addr_t) (mp->phys_addr + (phys_addr_t) ((char*) obj - (char*) mp))) return -1; - rte_mempool_dump(mp); printf("put the object back\n"); rte_mempool_put(mp, obj); @@ -421,9 +234,10 @@ static int test_mempool_single_producer(void) break; rte_spinlock_lock(&scsp_spinlock); for (i = 0; i < MAX_KEEP; i ++) { - if (NULL != scsp_obj_table[i]) + if (NULL != scsp_obj_table[i]) { obj = scsp_obj_table[i]; break; + } } rte_spinlock_unlock(&scsp_spinlock); if (i >= MAX_KEEP) { @@ -664,32 +478,6 @@ test_mempool(void) if (test_mempool_basic_ex(mp_nocache) < 0) return -1; - /* performance test with 1, 2 and max cores */ - printf("start performance test (without cache)\n"); - mp = mp_nocache; - - if (do_one_mempool_test(1) < 0) - return -1; - - if (do_one_mempool_test(2) < 0) - return -1; - - if (do_one_mempool_test(rte_lcore_count()) < 0) - return -1; - - /* performance test with 1, 2 and max cores */ - printf("start performance test (with cache)\n"); - mp = mp_cache; - - if (do_one_mempool_test(1) < 0) - return -1; - - if (do_one_mempool_test(2) < 0) - return -1; - - if (do_one_mempool_test(rte_lcore_count()) < 0) - return -1; - /* mempool operation test based on single producer and single comsumer */ if (test_mempool_sp_sc() < 0) return -1; diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c new file mode 100644 index 0000000000..a1283add6c --- /dev/null +++ b/app/test/test_mempool_perf.c @@ -0,0 +1,334 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test.h" + +/* + * Mempool performance + * ======= + * + * Each core get *n_keep* objects per bulk of *n_get_bulk*. Then, + * objects are put back in the pool per bulk of *n_put_bulk*. + * + * This sequence is done during TIME_S seconds. + * + * This test is done on the following configurations: + * + * - Cores configuration (*cores*) + * + * - One core with cache + * - Two cores with cache + * - Max. cores with cache + * - One core without cache + * - Two cores without cache + * - Max. cores without cache + * + * - Bulk size (*n_get_bulk*, *n_put_bulk*) + * + * - Bulk get from 1 to 32 + * - Bulk put from 1 to 32 + * + * - Number of kept objects (*n_keep*) + * + * - 32 + * - 128 + */ + +#define N 65536 +#define TIME_S 5 +#define MEMPOOL_ELT_SIZE 2048 +#define MAX_KEEP 128 +#define MEMPOOL_SIZE ((RTE_MAX_LCORE*(MAX_KEEP+RTE_MEMPOOL_CACHE_MAX_SIZE))-1) + +static struct rte_mempool *mp; +static struct rte_mempool *mp_cache, *mp_nocache; + +static rte_atomic32_t synchro; + +/* number of objects in one bulk operation (get or put) */ +static unsigned n_get_bulk; +static unsigned n_put_bulk; + +/* number of objects retrived from mempool before putting them back */ +static unsigned n_keep; + +/* number of enqueues / dequeues */ +struct mempool_test_stats { + unsigned enq_count; +} __rte_cache_aligned; + +static struct mempool_test_stats stats[RTE_MAX_LCORE]; + +/* + * save the object number in the first 4 bytes of object data. All + * other bytes are set to 0. + */ +static void +my_obj_init(struct rte_mempool *mp, __attribute__((unused)) void *arg, + void *obj, unsigned i) +{ + uint32_t *objnum = obj; + memset(obj, 0, mp->elt_size); + *objnum = i; +} + +static int +per_lcore_mempool_test(__attribute__((unused)) void *arg) +{ + void *obj_table[MAX_KEEP]; + unsigned i, idx; + unsigned lcore_id = rte_lcore_id(); + int ret; + uint64_t start_cycles, end_cycles; + uint64_t time_diff = 0, hz = rte_get_hpet_hz(); + + /* n_get_bulk and n_put_bulk must be divisors of n_keep */ + if (((n_keep / n_get_bulk) * n_get_bulk) != n_keep) + return -1; + if (((n_keep / n_put_bulk) * n_put_bulk) != n_keep) + return -1; + + stats[lcore_id].enq_count = 0; + + /* wait synchro for slaves */ + if (lcore_id != rte_get_master_lcore()) + while (rte_atomic32_read(&synchro) == 0); + + start_cycles = rte_get_hpet_cycles(); + + while (time_diff/hz < TIME_S) { + for (i = 0; likely(i < (N/n_keep)); i++) { + /* get n_keep objects by bulk of n_bulk */ + idx = 0; + while (idx < n_keep) { + ret = rte_mempool_get_bulk(mp, &obj_table[idx], + n_get_bulk); + if (unlikely(ret < 0)) { + rte_mempool_dump(mp); + rte_ring_dump(mp->ring); + /* in this case, objects are lost... */ + return -1; + } + idx += n_get_bulk; + } + + /* put the objects back */ + idx = 0; + while (idx < n_keep) { + rte_mempool_put_bulk(mp, &obj_table[idx], + n_put_bulk); + idx += n_put_bulk; + } + } + end_cycles = rte_get_hpet_cycles(); + time_diff = end_cycles - start_cycles; + stats[lcore_id].enq_count += N; + } + + return 0; +} + +/* launch all the per-lcore test, and display the result */ +static int +launch_cores(unsigned cores) +{ + unsigned lcore_id; + unsigned rate; + int ret; + unsigned cores_save = cores; + + rte_atomic32_set(&synchro, 0); + + /* reset stats */ + memset(stats, 0, sizeof(stats)); + + printf("mempool_autotest cache=%u cores=%u n_get_bulk=%u " + "n_put_bulk=%u n_keep=%u ", + (unsigned) mp->cache_size, cores, n_get_bulk, n_put_bulk, n_keep); + + if (rte_mempool_count(mp) != MEMPOOL_SIZE) { + printf("mempool is not full\n"); + return -1; + } + + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (cores == 1) + break; + cores--; + rte_eal_remote_launch(per_lcore_mempool_test, + NULL, lcore_id); + } + + /* start synchro and launch test on master */ + rte_atomic32_set(&synchro, 1); + + ret = per_lcore_mempool_test(NULL); + + cores = cores_save; + RTE_LCORE_FOREACH_SLAVE(lcore_id) { + if (cores == 1) + break; + cores--; + if (rte_eal_wait_lcore(lcore_id) < 0) + ret = -1; + } + + if (ret < 0) { + printf("per-lcore test returned -1\n"); + return -1; + } + + rate = 0; + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) + rate += (stats[lcore_id].enq_count / TIME_S); + + printf("rate_persec=%u\n", rate); + + return 0; +} + +/* for a given number of core, launch all test cases */ +static int +do_one_mempool_test(unsigned cores) +{ + unsigned bulk_tab_get[] = { 1, 4, 32, 0 }; + unsigned bulk_tab_put[] = { 1, 4, 32, 0 }; + unsigned keep_tab[] = { 32, 128, 0 }; + unsigned *get_bulk_ptr; + unsigned *put_bulk_ptr; + unsigned *keep_ptr; + int ret; + + for (get_bulk_ptr = bulk_tab_get; *get_bulk_ptr; get_bulk_ptr++) { + for (put_bulk_ptr = bulk_tab_put; *put_bulk_ptr; put_bulk_ptr++) { + for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) { + + n_get_bulk = *get_bulk_ptr; + n_put_bulk = *put_bulk_ptr; + n_keep = *keep_ptr; + ret = launch_cores(cores); + + if (ret < 0) + return -1; + } + } + } + return 0; +} + +int +test_mempool_perf(void) +{ + rte_atomic32_init(&synchro); + + /* create a mempool (without cache) */ + if (mp_nocache == NULL) + mp_nocache = rte_mempool_create("perf_test_nocache", MEMPOOL_SIZE, + MEMPOOL_ELT_SIZE, 0, 0, + NULL, NULL, + my_obj_init, NULL, + SOCKET_ID_ANY, 0); + if (mp_nocache == NULL) + return -1; + + /* create a mempool (with cache) */ + if (mp_cache == NULL) + mp_cache = rte_mempool_create("perf_test_cache", MEMPOOL_SIZE, + MEMPOOL_ELT_SIZE, + RTE_MEMPOOL_CACHE_MAX_SIZE, 0, + NULL, NULL, + my_obj_init, NULL, + SOCKET_ID_ANY, 0); + if (mp_cache == NULL) + return -1; + + /* performance test with 1, 2 and max cores */ + printf("start performance test (without cache)\n"); + mp = mp_nocache; + + if (do_one_mempool_test(1) < 0) + return -1; + + if (do_one_mempool_test(2) < 0) + return -1; + + if (do_one_mempool_test(rte_lcore_count()) < 0) + return -1; + + /* performance test with 1, 2 and max cores */ + printf("start performance test (with cache)\n"); + mp = mp_cache; + + if (do_one_mempool_test(1) < 0) + return -1; + + if (do_one_mempool_test(2) < 0) + return -1; + + if (do_one_mempool_test(rte_lcore_count()) < 0) + return -1; + + rte_mempool_list_dump(); + + return 0; +} diff --git a/mk/rte.sdkroot.mk b/mk/rte.sdkroot.mk index bcf9fa594c..91e7debb63 100644 --- a/mk/rte.sdkroot.mk +++ b/mk/rte.sdkroot.mk @@ -94,8 +94,8 @@ config: test: $(Q)$(MAKE) -f $(RTE_SDK)/mk/rte.sdktest.mk test -.PHONY: fast_test ring_test mempool_test -fast_test ring_test mempool_test: +.PHONY: fast_test ring_test mempool_test perf_test coverage +fast_test ring_test mempool_test perf_test coverage: $(Q)$(MAKE) -f $(RTE_SDK)/mk/rte.sdktest.mk $@ .PHONY: testall diff --git a/mk/rte.sdktest.mk b/mk/rte.sdktest.mk index 0fda37f67f..143f6d126e 100644 --- a/mk/rte.sdktest.mk +++ b/mk/rte.sdktest.mk @@ -49,17 +49,37 @@ DIR := $(shell basename $(RTE_OUTPUT)) # PHONY: test fast_test -fast_test: BLACKLIST=-Ring,Mempool +coverage: BLACKLIST=-Mempool_perf,Memcpy_perf,Hash_perf +fast_test: BLACKLIST=-Ring,Mempool_perf,Memcpy_perf,Hash_perf ring_test: WHITELIST=Ring -mempool_test: WHITELIST=Mempool -test fast_test ring_test mempool_test: +mempool_test: WHITELIST=Mempool,Mempool_perf +perf_test:WHITELIST=Mempool_perf,Memcpy_perf,Hash_perf,Ring +test fast_test ring_test mempool_test perf_test: @mkdir -p $(AUTOTEST_DIR) ; \ cd $(AUTOTEST_DIR) ; \ if [ -f $(RTE_OUTPUT)/app/test ]; then \ python $(RTE_SDK)/app/test/autotest.py \ $(RTE_OUTPUT)/app/test \ - $(DIR) $(RTE_TARGET) \ + $(RTE_TARGET) \ $(BLACKLIST) $(WHITELIST); \ else \ echo "No test found, please do a 'make build' first, or specify O=" ; \ fi + +# this is a special target to ease the pain of running coverage tests +# this runs all the autotests, cmdline_test script and dump_cfg +coverage: + @mkdir -p $(AUTOTEST_DIR) ; \ + cd $(AUTOTEST_DIR) ; \ + if [ -f $(RTE_OUTPUT)/app/test ]; then \ + python $(RTE_SDK)/app/cmdline_test/cmdline_test.py \ + $(RTE_OUTPUT)/app/cmdline_test; \ + ulimit -S -n 100 ; \ + python $(RTE_SDK)/app/test/autotest.py \ + $(RTE_OUTPUT)/app/test \ + $(RTE_TARGET) \ + $(BLACKLIST) $(WHITELIST) ; \ + $(RTE_OUTPUT)/app/dump_cfg --file-prefix=ring_perf ; \ + else \ + echo "No test found, please do a 'make build' first, or specify O=" ;\ + fi \ No newline at end of file diff --git a/mk/rte.sdktestall.mk b/mk/rte.sdktestall.mk index a3ff2d482d..16762ed73f 100644 --- a/mk/rte.sdktestall.mk +++ b/mk/rte.sdktestall.mk @@ -55,4 +55,4 @@ testall: $(TESTALL_TARGETS) %_testall: @echo ================== Test $* - $(Q)$(MAKE) test O=$* + $(Q)$(MAKE) fast_test O=$* -- 2.20.1