From: Pablo de Lara <pablo.de.lara.guarch@intel.com>
To: dev@dpdk.org
Subject: [dpdk-dev] [PATCH 3/3] app/test: Added unit tests for Thread Safe Hash library
Date: Thu, 18 Sep 2014 11:34:31 +0100 [thread overview]
Message-ID: <1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com> (raw)
In-Reply-To: <1411036471-3822-1-git-send-email-pablo.de.lara.guarch@intel.com>
Added 3 new unit tests:
- Functional unit test: Tests creation and handling of
a hash table with a single thread.
- Performance unit tests: Benchmark hash operations
add/delete/lookup, returning number of CPU cycles/operation.
- Multi thread unit tests: Checks there is no data corruption
due to multiple threads working on the same hash table.
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
---
app/test/Makefile | 4 +
app/test/test_tshash_func.c | 1117 +++++++++++++++++++++++++++++++++++
app/test/test_tshash_multi_thread.c | 351 +++++++++++
app/test/test_tshash_perf.c | 631 ++++++++++++++++++++
4 files changed, 2103 insertions(+), 0 deletions(-)
create mode 100644 app/test/test_tshash_func.c
create mode 100644 app/test/test_tshash_multi_thread.c
create mode 100644 app/test/test_tshash_perf.c
diff --git a/app/test/Makefile b/app/test/Makefile
index 37a3772..71dd7c2 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -83,6 +83,10 @@ SRCS-y += test_memcpy_perf.c
SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash.c
SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_perf.c
+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_func.c
+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_perf.c
+SRCS-$(CONFIG_RTE_LIBRTE_THREAD_SAFE_HASH) += test_tshash_multi_thread.c
+
SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm.c
SRCS-$(CONFIG_RTE_LIBRTE_LPM) += test_lpm6.c
diff --git a/app/test/test_tshash_func.c b/app/test/test_tshash_func.c
new file mode 100644
index 0000000..7ec2e12
--- /dev/null
+++ b/app/test/test_tshash_func.c
@@ -0,0 +1,1117 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_ip.h>
+#include <rte_string_fns.h>
+#include <cmdline_parse.h>
+
+#include <rte_tshash.h>
+#include <rte_hash.h>
+#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
+#include <rte_hash_crc.h>
+#endif
+#include <rte_jhash.h>
+
+#include "test.h"
+
+#define MAX_KEYS 1024
+#define NUM_KEYS 64
+
+#if defined RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+
+#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
+static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc};
+static const char * const hash_func_strings[] = {"jhash", "crc"};
+#else
+static rte_hash_function hashtest_funcs[] = {rte_jhash};
+static const char * const hash_func_strings[] = {"jhash"};
+#endif
+
+
+/*
+ * Check condition and return an error if true.
+ */
+#define RETURN_IF_ERROR(cond, str, ...) do { \
+ if (cond) { \
+ printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
+ return -1; \
+ } \
+} while (0)
+
+/*
+ * Hash function that always returns the same value, to easily test what
+ * happens when a bucket is full.
+ */
+static uint32_t pseudo_hash(__attribute__((unused)) const void *keys,
+ __attribute__((unused)) uint32_t key_len,
+ __attribute__((unused)) uint32_t init_val)
+{
+ return 3;
+}
+
+static void *generate_key(uint8_t num_bytes)
+{
+ char *key = rte_zmalloc(NULL, num_bytes, 0);
+ unsigned i;
+
+ for (i = 0; i < num_bytes; i++)
+ key[i] = rand() % 255;
+
+ return (void *)key;
+}
+
+/* Parameters used for hash table in unit test functions. Name set later. */
+static struct rte_tshash_parameters {
+ char name[RTE_TSHASH_NAMESIZE];
+ unsigned max_entries;
+ unsigned key_len;
+ uint8_t socket_id;
+
+} ut_params;
+
+static uint8_t key_sizes[] = {16, 32, 48, 64, 96, 128};
+static struct rte_tshash *hash_tables[RTE_DIM(key_sizes)];
+static uint8_t burst_sizes[] = {13, 16, 32, 64};
+
+static struct rte_tshash_extra_args e_args = {
+ .malloc_mem = 0,
+ .max_load_factor = 0,
+ .rte_tshash_cmp_eq = NULL,
+ .hash_func = NULL
+
+};
+
+static int
+create_hash_tables(void)
+{
+ unsigned i;
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ hash_tables[i] = NULL;
+ sprintf(ut_params.name, "test_k%d", key_sizes[i]);
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = key_sizes[i];
+ ut_params.socket_id = rte_socket_id();
+ hash_tables[i] = rte_tshash_find_existing(ut_params.name);
+ if (hash_tables[i] != NULL)
+ rte_tshash_reset(hash_tables[i]);
+ else
+ hash_tables[i] = rte_tshash_create(ut_params.name,
+ ut_params.max_entries, ut_params.key_len,
+ ut_params.socket_id, NULL);
+ RETURN_IF_ERROR(hash_tables[i] == NULL, "hash creation failed");
+ }
+ return 0;
+}
+/*
+ * Basic sequence of operations for a single key:
+ * - add
+ * - lookup (hit)
+ * - delete
+ * - lookup (miss)
+ */
+static int
+test_hash_add_lookup_delete(void)
+{
+ /* Test with standard add/lookup/delete functions */
+ unsigned i;
+ uint64_t hash_value, ret_data, data;
+ void *key;
+
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ key = generate_key(key_sizes[i]);
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key(hash_tables[i], key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup(hash_tables[i], key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to find key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key(hash_tables[i], key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup(hash_tables[i], key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(hash_tables[i]);
+ }
+
+ /* Repeat test with precomputed hash values */
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ key = generate_key(key_sizes[i]);
+ hash_value = rte_rand() % MAX_KEYS;
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key_with_hash(hash_tables[i], hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(hash_tables[i], hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to find key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key_with_hash(hash_tables[i], hash_value, key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup_with_hash(hash_tables[i], hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(hash_tables[i]);
+ }
+
+ return 0;
+}
+
+/*
+ * Sequence of operations for a single key:
+ * - delete: miss
+ * - add
+ * - lookup: hit
+ * - add: miss
+ * - lookup: hit
+ * - delete: hit
+ * - delete: miss
+ * - lookup: miss
+ */
+static int
+test_hash_add_lookup_delete_miss(void)
+{
+ unsigned i;
+ uint64_t hash_value, ret_data, data;
+ void *key;
+
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ key = generate_key(key_sizes[i]);
+ hash_value = rte_rand() % MAX_KEYS;
+ data = rte_rand();
+
+ if (0 == rte_tshash_del_key_with_hash(hash_tables[i],
+ hash_value, key, NULL)) {
+ rte_free(key);
+ printf("Error: Deleted key of %d bytes that should not exist\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_add_key_with_hash(hash_tables[i],
+ hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(hash_tables[i],
+ hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to lookup key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 == rte_tshash_add_key_with_hash(hash_tables[i],
+ hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Added key of %d bytes that already existed\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(hash_tables[i],
+ hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to lookup key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key_with_hash(hash_tables[i],
+ hash_value, key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 == rte_tshash_del_key_with_hash(hash_tables[i],
+ hash_value, key, NULL)) {
+ rte_free(key);
+ printf("Error: Deleted key of %d bytes that should not exist\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup_with_hash(hash_tables[i],
+ hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(hash_tables[i]);
+ }
+
+ return 0;
+}
+
+/*
+ * Sequence of operations for a single key:
+ * - add
+ * - lookup: hit
+ * - add: update
+ * - lookup: hit
+ * - delete: hit
+ * - lookup: miss
+ */
+static int
+test_hash_add_lookup_update_delete_miss(void)
+{
+ struct rte_tshash *hash_tables_update[RTE_DIM(key_sizes)];
+
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.socket_id = 0;
+ e_args.malloc_mem = 0;
+ e_args.max_load_factor = 0.5;
+ e_args.rte_tshash_cmp_eq = NULL;
+ e_args.hash_func = NULL;
+ e_args.update_add = 1;
+
+ unsigned i;
+ uint64_t hash_value, ret_data, data;
+ void *key;
+
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ sprintf(ut_params.name, "test_k%d_update", key_sizes[i]);
+ ut_params.key_len = key_sizes[i];
+
+ hash_tables_update[i] = rte_tshash_find_existing(ut_params.name);
+ if (hash_tables_update[i] != NULL)
+ rte_tshash_reset(hash_tables_update[i]);
+ else
+ hash_tables_update[i] = rte_tshash_create(ut_params.name,
+ ut_params.max_entries,
+ ut_params.key_len,
+ ut_params.socket_id,
+ &e_args);
+ RETURN_IF_ERROR(hash_tables_update[i] == NULL, "hash creation failed");
+
+ key = generate_key(key_sizes[i]);
+ hash_value = rte_rand() % MAX_KEYS;
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key_with_hash(hash_tables_update[i],
+ hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(hash_tables_update[i],
+ hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to lookup key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ data = rte_rand() % MAX_KEYS;
+ if (0 != rte_tshash_add_key_with_hash(hash_tables_update[i],
+ hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to update data of key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(hash_tables_update[i], hash_value,
+ key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to lookup key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key_with_hash(hash_tables_update[i], hash_value,
+ key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup_with_hash(hash_tables_update[i], hash_value,
+ key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n", key_sizes[i]);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(hash_tables_update[i]);
+ }
+
+ return 0;
+}
+
+/*
+ * Sequence of operations for find existing hash table
+ *
+ * - find existing table: hit
+ * - find non-existing table: miss
+ *
+ */
+static int
+test_hash_find_existing(void)
+{
+ struct rte_tshash *result = NULL;
+ unsigned i;
+ char test_name[RTE_TSHASH_NAMESIZE];
+
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ /* Try to find existing hash table */
+ sprintf(test_name, "test_k%d", key_sizes[i]);
+ result = rte_tshash_find_existing(test_name);
+ RETURN_IF_ERROR(result != hash_tables[i], "could not find existing hash table");
+ }
+
+ /* Try to find non-existing hash table */
+ result = rte_tshash_find_existing("hash_find_non_existing");
+ RETURN_IF_ERROR(!(result == NULL), "found table that shouldn't exist");
+
+ return 0;
+}
+
+/*
+ * Sequence of operations for 5 keys
+ * - add keys
+ * - lookup keys: hit
+ * - delete keys : hit
+ * - lookup keys: miss
+ */
+static int
+test_hash_burst(void)
+{
+ unsigned i, j, k;
+ void *keys[NUM_KEYS];
+ uint64_t data[NUM_KEYS];
+ uint64_t ret_data[NUM_KEYS];
+ uint64_t hash_values[NUM_KEYS];
+ uint64_t *hash_values_ptrs[NUM_KEYS];
+ uint64_t lookup_mask, hit_mask;
+ uint8_t hits;
+
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ for (j = 0; j < NUM_KEYS; j++) {
+ keys[j] = generate_key(key_sizes[i]);
+ data[j] = rte_rand();
+ if (0 != rte_tshash_add_key(hash_tables[i], keys[j], data[j])) {
+ printf("Error: Failed to add key of %d bytes\n", key_sizes[i]);
+ goto fail_burst;
+ }
+ }
+ for (j = 0; j < RTE_DIM(burst_sizes); j++) {
+ if (burst_sizes[j] == 64)
+ lookup_mask = 0xffffffffffffffff;
+ else
+ lookup_mask = (1llu << burst_sizes[j]) - 1;
+
+ hits = rte_tshash_lookup_bulk(hash_tables[i], lookup_mask,
+ (const void * const *)keys, ret_data,
+ &hit_mask);
+ if (hits != burst_sizes[j]) {
+ printf("Error: Failed to find %d key(s) of %d bytes\n",
+ burst_sizes[j] - hits, key_sizes[i]);
+ goto fail_burst;
+ }
+ for (k = 0; k < burst_sizes[j]; k++) {
+ if (unlikely(ret_data[j] != data[j])) {
+ printf("Error with value returned from lookup of key of %d bytes",
+ key_sizes[i]);
+ goto fail_burst;
+ }
+ }
+ }
+
+ for (j = 0; j < NUM_KEYS; j++) {
+ if (0 != rte_tshash_del_key(hash_tables[i], keys[j], NULL)) {
+ printf("Error: Failed to delete key of %d bytes\n", key_sizes[i]);
+ goto fail_burst;
+ }
+ }
+
+ lookup_mask = 0xffffffffffffffff;
+ hits = rte_tshash_lookup_bulk(hash_tables[i], lookup_mask,
+ (const void * const *)keys, ret_data,
+ &hit_mask);
+ if (0 != hits) {
+ printf("Error: Found %d key(s) of %d bytes that should not exist\n",
+ hits, key_sizes[i]);
+ goto fail_burst;
+ }
+
+ for (j = 0; j < NUM_KEYS; j++)
+ rte_free(keys[j]);
+ rte_tshash_reset(hash_tables[i]);
+ }
+
+ /* Repeat test with precomputed hash values */
+ for (i = 0; i < RTE_DIM(key_sizes); i++) {
+ for (j = 0; j < NUM_KEYS; j++) {
+ keys[j] = generate_key(key_sizes[i]);
+ hash_values[j] = rte_rand() % MAX_KEYS;
+ hash_values_ptrs[j] = &hash_values[j];
+ data[j] = rte_rand();
+ if (0 != rte_tshash_add_key_with_hash(hash_tables[i], hash_values[j],
+ keys[j], data[j])) {
+ printf("Error: Failed to add key of %d bytes\n", key_sizes[i]);
+ goto fail_burst;
+ }
+ }
+ for (j = 0; j < RTE_DIM(burst_sizes); j++) {
+ if (burst_sizes[j] == 64)
+ lookup_mask = 0xffffffffffffffff;
+ else
+ lookup_mask = (1llu << burst_sizes[j]) - 1;
+
+ hits = rte_tshash_lookup_bulk_with_hash(hash_tables[i], lookup_mask,
+ (uint64_t * const *)hash_values_ptrs,
+ (const void * const *)keys, ret_data,
+ &hit_mask);
+ if (hits != burst_sizes[j]) {
+ printf("Error: Failed to find %d key(s) of %d bytes\n",
+ burst_sizes[j] - hits, key_sizes[i]);
+ goto fail_burst;
+ }
+ for (k = 0; k < burst_sizes[j]; k++) {
+ if (unlikely(ret_data[j] != data[j])) {
+ printf("Error with value returned from lookup of key of %d bytes",
+ key_sizes[i]);
+ goto fail_burst;
+ }
+ }
+ }
+
+ for (j = 0; j < NUM_KEYS; j++) {
+ if (0 != rte_tshash_del_key_with_hash(hash_tables[i], hash_values[j],
+ keys[j], NULL)) {
+ printf("Error: Failed to delete key of %d bytes\n", key_sizes[i]);
+ goto fail_burst;
+ return -1;
+ }
+ }
+
+ lookup_mask = 0xffffffffffffffff;
+ hits = rte_tshash_lookup_bulk_with_hash(hash_tables[i], lookup_mask,
+ (uint64_t * const *)hash_values_ptrs,
+ (const void * const *)keys, ret_data,
+ &hit_mask);
+ if (0 != hits) {
+ printf("Error: Found %d key(s) of %d bytes that should not exist\n",
+ hits, key_sizes[i]);
+ goto fail_burst;
+ }
+
+ for (j = 0; j < NUM_KEYS; j++)
+ rte_free(keys[j]);
+ rte_tshash_reset(hash_tables[i]);
+ }
+
+ return 0;
+
+fail_burst:
+ for (j = 0; j < NUM_KEYS; j++)
+ rte_free(keys[j]);
+ return -1;
+}
+
+/*
+ * 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 in first level, 5th in second one
+ * - lookup the 5 keys: 4 hits in first level, 1 in second one
+ * - delete the 5 keys: 5 OK
+ * - lookup the 5 keys: 5 misses
+ */
+static int
+test_hash_full_bucket(void)
+{
+ struct rte_tshash *handle;
+ unsigned i;
+ void *keys[NUM_KEYS];
+ uint64_t data[NUM_KEYS];
+ uint64_t ret_data[NUM_KEYS];
+
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = key_sizes[0];
+ ut_params.socket_id = 0;
+ sprintf(ut_params.name, "test_full_bucket");
+ e_args.max_load_factor = 0.5;
+ e_args.hash_func = pseudo_hash;
+
+ handle = rte_tshash_find_existing(ut_params.name);
+ if (handle != NULL)
+ rte_tshash_reset(handle);
+ else
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, &e_args);
+ RETURN_IF_ERROR(handle == NULL, "hash creation failed");
+
+
+ /* Fill bucket */
+ for (i = 0; i < RTE_TSHASH_BUCKET_SIZE; i++) {
+ keys[i] = generate_key(ut_params.key_len);
+ data[i] = rte_rand();
+
+ if (0 != rte_tshash_add_key(handle, keys[i], data[i])) {
+ printf("Error: Failed to add key of %d bytes\n", ut_params.key_len);
+ goto fail_burst;
+ }
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(handle);
+ if (stats->used_slots != (i+1) || stats->num_extra_buckets != 0) {
+ printf("Error: used_slots = %u, expected = %u; extra_buckets = %u"
+ " expected = 0 \n", stats->used_slots, i+1,
+ stats->num_extra_buckets);
+ goto fail_burst;
+ }
+#endif
+ }
+
+ /* This new entry should go to the next bucket */
+ keys[RTE_TSHASH_BUCKET_SIZE] = generate_key(ut_params.key_len);
+ data[RTE_TSHASH_BUCKET_SIZE] = rte_rand();
+
+ if (0 != rte_tshash_add_key(handle, keys[RTE_TSHASH_BUCKET_SIZE],
+ data[RTE_TSHASH_BUCKET_SIZE])) {
+ printf("Error: Failed to add key of %d bytes\n", ut_params.key_len);
+ goto fail_burst;
+ }
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(handle);
+ if (stats->used_slots != (RTE_TSHASH_BUCKET_SIZE+1)
+ || stats->num_extra_buckets != 1) {
+ printf("Error: used_slots = %u, expected = %u; extra_buckets = %u"
+ " expected = 1 \n", stats->used_slots, RTE_TSHASH_BUCKET_SIZE+1,
+ stats->num_extra_buckets);
+ goto fail_burst;
+ }
+#endif
+
+ /* Lookup */
+ for (i = 0; i < RTE_TSHASH_BUCKET_SIZE+1; i++) {
+ if (0 != rte_tshash_lookup(handle, keys[i], &ret_data[i])) {
+ printf("Error: Failed to find key of %d bytes\n", ut_params.key_len);
+ goto fail_burst;
+ }
+ if (ret_data[i] != data[i]) {
+ printf("Error: Data returned was not the one expected\n");
+ goto fail_burst;
+ }
+ }
+
+ /* Delete */
+ for (i = 0; i < RTE_TSHASH_BUCKET_SIZE+1; i++) {
+ if (0 != rte_tshash_del_key(handle, keys[i], NULL)) {
+ printf("Error: Failed to delete key of %d bytes\n", ut_params.key_len);
+ goto fail_burst;
+ }
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(handle);
+ if (stats->used_slots != (RTE_TSHASH_BUCKET_SIZE-i)
+ || stats->num_extra_buckets != 1) {
+ printf("Error: used_slots = %u, expected = %u; extra_buckets = %u"
+ " expected = 1 \n", stats->used_slots, RTE_TSHASH_BUCKET_SIZE-i,
+ stats->num_extra_buckets);
+ goto fail_burst;
+ }
+#endif
+ }
+
+ /* Lookup */
+ for (i = 0; i < RTE_TSHASH_BUCKET_SIZE; i++) {
+ if (0 == rte_tshash_lookup(handle, keys[i], &ret_data[i])) {
+ printf("Error: Found key after deleting key of %d bytes\n",
+ ut_params.key_len);
+ goto fail_burst;
+ }
+ }
+ for (i = 0; i < RTE_TSHASH_BUCKET_SIZE + 1; i++)
+ rte_free(keys[i]);
+
+ return 0;
+fail_burst:
+ for (i = 0; i < RTE_TSHASH_BUCKET_SIZE + 1; i++)
+ rte_free(keys[i]);
+ return -1;
+}
+
+
+/*
+ * Do tests for hash creation with bad parameters.
+ */
+static int
+test_hash_creation_with_bad_parameters(void)
+{
+ struct rte_tshash *handle;
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = key_sizes[0];
+ ut_params.socket_id = 0;
+
+ handle = rte_tshash_create(NULL, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, NULL);
+
+ RETURN_IF_ERROR(handle != NULL,
+ "Impossible creating hash sucessfully without a name\n");
+
+ sprintf(ut_params.name, "creation_with_bad_parameters_0");
+ ut_params.max_entries = RTE_TSHASH_MIN_ENTRIES - 1;
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, NULL);
+
+ RETURN_IF_ERROR(handle != NULL,
+ "Impossible creating hash sucessfully with maximum number of entries"
+ "less than minimum required\n");
+
+ sprintf(ut_params.name, "creation_with_bad_parameters_1");
+ ut_params.max_entries = MAX_KEYS;
+ e_args.max_load_factor = RTE_TSHASH_MAXIMUM_LOAD_FACTOR + 0.1;
+ e_args.malloc_mem = 0;
+ e_args.rte_tshash_cmp_eq = NULL;
+ e_args.hash_func = NULL;
+
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, &e_args);
+
+ RETURN_IF_ERROR(handle != NULL,
+ "Impossible creating hash sucessfully with max_load_factor"
+ "in parameter exceeded\n");
+
+ sprintf(ut_params.name, "creation_with_bad_parameters_2");
+ e_args.max_load_factor = RTE_TSHASH_MAXIMUM_LOAD_FACTOR;
+ ut_params.key_len = 13;
+
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, &e_args);
+
+ RETURN_IF_ERROR(handle != NULL,
+ "Impossible creating hash sucessfully wif key size is not multiple"
+ "of 16 and there is no user defined key compare function\n");
+
+ sprintf(ut_params.name, "creation_with_bad_parameters_3");
+ ut_params.key_len = 0;
+
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, NULL);
+
+ RETURN_IF_ERROR(handle != NULL,
+ "Impossible creating hash sucessfully if key_len in parameter is zero\n");
+
+ sprintf(ut_params.name, "creation_with_bad_parameters_4");
+ ut_params.socket_id = RTE_MAX_NUMA_NODES;
+ ut_params.key_len = key_sizes[0];
+
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, NULL);
+
+ RETURN_IF_ERROR(handle != NULL,
+ "Impossible creating hash sucessfully with invalid socket\n");
+
+ return 0;
+}
+
+/*
+ * Test the creation of a hash table with malloc function.
+ */
+static int
+test_hash_creation_with_malloc(void)
+{
+ struct rte_tshash *handle;
+
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = key_sizes[0];
+ ut_params.socket_id = 0;
+ sprintf(ut_params.name, "test_malloc");
+ e_args.malloc_mem = 1;
+ e_args.max_load_factor = 0.5;
+ e_args.rte_tshash_cmp_eq = NULL;
+ e_args.hash_func = NULL;
+
+ handle = rte_tshash_find_existing(ut_params.name);
+ if (handle != NULL)
+ rte_tshash_reset(handle);
+ else
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id, &e_args);
+ RETURN_IF_ERROR(handle == NULL, "hash creation with malloc failed");
+
+ return 0;
+}
+
+static int
+generic_key_cmp(const void *key1, const void *key2, uint8_t key_size)
+{
+ return !memcmp(key1, key2, key_size);
+}
+
+/*
+ * Test add/lookup/delete functions with user-defined key compare function.
+ */
+static int
+test_hash_odd_key_size(void)
+{
+ struct rte_tshash *handle;
+ uint64_t hash_value, ret_data, data;
+ void *key;
+
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = 26;
+ ut_params.socket_id = 0;
+ sprintf(ut_params.name, "test_odd_key_size");
+ e_args.max_load_factor = 0.5;
+ e_args.rte_tshash_cmp_eq = generic_key_cmp;
+
+ handle = rte_tshash_find_existing(ut_params.name);
+ if (handle != NULL)
+ rte_tshash_reset(handle);
+ else
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id,
+ &e_args);
+ RETURN_IF_ERROR(handle == NULL, "hash creation with malloc failed");
+
+ /* Test with standard add/lookup/delete functions */
+ key = generate_key(ut_params.key_len);
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key(handle, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup(handle, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to find key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key(handle, key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup(handle, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n",
+ ut_params.key_len);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(handle);
+
+ /* Repeat test with precomputed hash values */
+ key = generate_key(ut_params.key_len);
+ hash_value = rte_rand() % MAX_KEYS;
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key_with_hash(handle, hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to find key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key_with_hash(handle, hash_value, key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n",
+ ut_params.key_len);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(handle);
+
+ return 0;
+}
+
+/*
+ * Test add/lookup/delete functions with different hash functions (jhash and crc).
+ */
+static int
+test_hash_different_hash_functions(void)
+{
+ unsigned i;
+ struct rte_tshash *handle;
+ uint64_t hash_value, ret_data, data;
+ void *key;
+
+ for (i = 0; i < RTE_DIM(hashtest_funcs); i++) {
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = key_sizes[0];
+ ut_params.socket_id = 0;
+ sprintf(ut_params.name, "test_tshash_%s", hash_func_strings[i]);
+ e_args.max_load_factor = 0.5;
+ e_args.rte_tshash_cmp_eq = NULL;
+ e_args.malloc_mem = 0;
+ e_args.hash_func = hashtest_funcs[i];
+
+ handle = rte_tshash_find_existing(ut_params.name);
+ if (handle != NULL)
+ rte_tshash_reset(handle);
+ else
+ handle = rte_tshash_create(ut_params.name, ut_params.max_entries,
+ ut_params.key_len, ut_params.socket_id,
+ &e_args);
+ RETURN_IF_ERROR(handle == NULL, "hash creation with %s hash function failed",
+ hash_func_strings[i]);
+
+ /* Test with standard add/lookup/delete functions */
+ key = generate_key(ut_params.key_len);
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key(handle, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup(handle, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to find key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key(handle, key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup(handle, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n",
+ ut_params.key_len);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(handle);
+
+ /* Repeat test with precomputed hash values */
+ key = generate_key(ut_params.key_len);
+ hash_value = rte_rand() % MAX_KEYS;
+ data = rte_rand();
+
+ if (0 != rte_tshash_add_key_with_hash(handle, hash_value, key, data)) {
+ rte_free(key);
+ printf("Error: Failed to add key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 != rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Failed to find key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+ if (ret_data != data) {
+ rte_free(key);
+ printf("Error: Data returned was not the one expected\n");
+ return -1;
+ }
+
+ if (0 != rte_tshash_del_key_with_hash(handle, hash_value, key, NULL)) {
+ rte_free(key);
+ printf("Error: Failed to delete key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ if (0 == rte_tshash_lookup_with_hash(handle, hash_value, key, &ret_data)) {
+ rte_free(key);
+ printf("Error: Found key after deleting key of %d bytes\n", ut_params.key_len);
+ return -1;
+ }
+
+ rte_free(key);
+ rte_tshash_reset(handle);
+ }
+
+ return 0;
+}
+
+/*
+ * Do all unit tests.
+ */
+static int
+test_tshash(void)
+{
+ printf("\n-- Test 0: Creating hash tables --\n");
+ if (create_hash_tables() < 0)
+ return -1;
+ printf("\n-- Test1: add, lookup, delete --\n");
+ if (test_hash_add_lookup_delete() < 0)
+ return -1;
+ printf("\n-- Test2: find existing hash table --\n");
+ if (test_hash_find_existing() < 0)
+ return -1;
+ printf("\n-- Test3: add, lookup, delete misses --\n");
+ if (test_hash_add_lookup_delete_miss() < 0)
+ return -1;
+ printf("\n-- Test4: add, lookup, update, delete misses --\n");
+ if (test_hash_add_lookup_update_delete_miss() < 0)
+ return -1;
+ printf("\n-- Test5: lookup bulk --\n");
+ if (test_hash_burst() < 0)
+ return -1;
+ printf("\n-- Test6: full bucket --\n");
+ if (test_hash_full_bucket() < 0)
+ return -1;
+ printf("\n-- Test7: create hash table with bad parameters --\n");
+ if (test_hash_creation_with_bad_parameters() < 0)
+ return -1;
+ printf("\n-- Test8: create hash table with malloc --\n");
+ if (test_hash_creation_with_malloc() < 0)
+ return -1;
+ printf("\n-- Test9: add, lookup, delete with odd key size (not multiple of 16 --\n");
+ if (test_hash_odd_key_size() < 0)
+ return -1;
+ printf("\n-- Test10: use different hash functions (jhash and CRC) --\n");
+ if (test_hash_different_hash_functions() < 0)
+ return -1;
+
+ return 0;
+}
+#else /* RTE_LIBRTE_THREAD_SAFE_HASH_STATS */
+
+static int
+test_tshash(void)
+{
+ printf("The Thread Safe Hash library stats are not included in this build\n");
+ return 0;
+}
+
+#endif /* RTE_LIBRTE_THREAD_SAFE_HASH_STATS */
+
+static struct test_command tshash_cmd = {
+ .command = "thread_safe_hash_autotest",
+ .callback = test_tshash,
+};
+REGISTER_TEST_COMMAND(tshash_cmd);
+
diff --git a/app/test/test_tshash_multi_thread.c b/app/test/test_tshash_multi_thread.c
new file mode 100644
index 0000000..eacc5d5
--- /dev/null
+++ b/app/test/test_tshash_multi_thread.c
@@ -0,0 +1,351 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 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 <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <cmdline_parse.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_atomic.h>
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <rte_random.h>
+#include <rte_tshash.h>
+
+#include "test.h"
+
+#define TOTAL_ADDITIONS 300000
+
+#define MAX_KEYS 65536
+#define KEYSIZE 16
+#define MULTIPLIER 13
+
+/* Parameters used for hash table in unit test functions. Name set later. */
+static struct hash_parameters {
+ char name[RTE_TSHASH_NAMESIZE];
+ unsigned max_entries;
+ unsigned key_len;
+ uint8_t socket_id;
+} ut_params;
+
+static struct ops_stats {
+ unsigned retries_add;
+ unsigned not_enough_mem_add;
+ unsigned already_exist_add;
+ unsigned no_free_slots_add;
+ unsigned success_add;
+
+ unsigned retries_lookup;
+ unsigned errors_lookup;
+ unsigned misses_lookup;
+ unsigned success_lookup;
+
+ unsigned num_additions;
+ unsigned num_deletions;
+ unsigned num_lookups;
+} stats;
+
+static struct rte_tshash *hash_table;
+
+union hash_key {
+ uint64_t hashval;
+ char key[KEYSIZE];
+};
+
+unsigned num_sec_threads;
+unsigned finished_additions;
+
+#define RETURN_IF_ERROR(cond, str, ...) do { \
+ if (cond) { \
+ printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
+ return -1; \
+ } \
+} while (0)
+
+static int
+create_hash_table(void)
+{
+ unsigned i;
+ hash_table = NULL;
+ sprintf(ut_params.name, "test_k%d_multi_thread", KEYSIZE);
+ ut_params.max_entries = MAX_KEYS;
+ ut_params.key_len = KEYSIZE;
+ ut_params.socket_id = rte_socket_id();
+
+ hash_table = rte_tshash_find_existing(ut_params.name);
+ if (hash_table != NULL)
+ rte_tshash_reset(hash_table);
+ else
+ hash_table = rte_tshash_create(ut_params.name,
+ ut_params.max_entries, ut_params.key_len,
+ ut_params.socket_id, NULL);
+ RETURN_IF_ERROR(hash_table == NULL, "hash creation failed");
+
+ for (i = 0; i < MAX_KEYS; i++) {
+ union hash_key k = { .key = {0} };
+ k.hashval = i;
+ RETURN_IF_ERROR(rte_tshash_add_key_with_hash(hash_table,
+ k.hashval, (void *)k.key, k.hashval * MULTIPLIER) != 0,
+ "Error adding entry number %u\n", i);
+ }
+
+ return 0;
+}
+
+static int
+test_add_single(__attribute__((unused)) void *arg)
+{
+ printf("Core %u is performing additions\n", rte_lcore_id());
+ while (stats.num_additions < TOTAL_ADDITIONS) {
+ union hash_key k = { .key = {0} };
+ k.hashval = MAX_KEYS/2-1 + (rte_rand() % 2) * MAX_KEYS/2;
+ if (0 == rte_tshash_add_key_with_hash(hash_table, k.hashval,
+ (void *)k.key,
+ k.hashval * MULTIPLIER))
+ stats.num_additions++;
+ }
+
+ printf("Core %u has finished performing additions\n", rte_lcore_id());
+ finished_additions = 1;
+ return 0;
+}
+
+static int
+test_add_multi(__attribute__((unused)) void *arg)
+{
+ int ret;
+ unsigned i;
+
+ printf("Core %u is performing additions\n", rte_lcore_id());
+
+ for (i = 0; i < MAX_KEYS; i++) {
+ union hash_key k = { .key = {0} };
+ k.hashval = MAX_KEYS/2-1 + rte_rand() * MAX_KEYS/2;
+
+ ret = rte_tshash_add_key_with_hash(hash_table, k.hashval,
+ (void *)k.key, k.hashval * MULTIPLIER);
+
+ switch (ret) {
+ case -EEXIST:
+ __sync_fetch_and_add(&stats.already_exist_add, 1);
+ break;
+ case -EAGAIN:
+ __sync_fetch_and_add(&stats.retries_add, 1);
+ break;
+ case -ENOMEM:
+ __sync_fetch_and_add(&stats.not_enough_mem_add, 1);
+ break;
+ case -ENOSPC:
+ __sync_fetch_and_add(&stats.no_free_slots_add, 1);
+ break;
+ default:
+ __sync_fetch_and_add(&stats.success_add, 1);
+ __sync_fetch_and_add(&stats.num_additions, 1);
+ }
+ }
+ return 0;
+}
+
+static int
+test_lookup(__attribute__((unused)) void *arg)
+{
+ int ret;
+
+ printf("Core %u is performing lookups\n", rte_lcore_id());
+ while (finished_additions == 0) {
+ union hash_key k = { .key = {0} };
+ uintptr_t retval = 0;
+ k.hashval = MAX_KEYS-1;
+
+ ret = rte_tshash_lookup_with_hash(hash_table, k.hashval,
+ (void *)k.key, &retval);
+ __sync_fetch_and_add(&stats.num_lookups, 1);
+ switch (ret) {
+ /* Miss */
+ case -1:
+ __sync_fetch_and_add(&stats.misses_lookup, 1);
+ break;
+ /* Out of sync */
+ case -EAGAIN:
+ __sync_fetch_and_add(&stats.retries_lookup, 1);
+ break;
+ /* Hit */
+ default:
+ /* Corrupted data */
+ if (retval != k.hashval * MULTIPLIER) {
+ __sync_fetch_and_add(&stats.errors_lookup, 1);
+ printf("Data corrupted!\n!");
+ printf("Expected %"PRIx64" but got %"PRIx64"\n", k.hashval * MULTIPLIER, retval);
+ } else
+ __sync_fetch_and_add(&stats.success_lookup, 1);
+ }
+ }
+ printf("Core %u has finished performing lookups\n", rte_lcore_id());
+
+ return 0;
+}
+
+static int
+test_delete(__attribute__((unused)) void *arg)
+{
+ printf("Core %u is performing deletions\n", rte_lcore_id());
+ while (finished_additions == 0) {
+ union hash_key k = { .key = {0} };
+ k.hashval = MAX_KEYS/2-1 + (rte_rand() % 2) * MAX_KEYS/2;
+ rte_tshash_del_key_with_hash(hash_table, k.hashval, (void *)k.key, NULL);
+ }
+ printf("Core %u has finished performing deletions\n", rte_lcore_id());
+
+ return 0;
+}
+
+/* 1 thread adding entries, 1 thread deleting entries and rest looking up */
+static int
+test_multi_lookup(void) {
+ unsigned i, j;
+
+ do {
+ j = 0;
+ finished_additions = 0;
+ /* Reset operation stats */
+ memset(&stats, 0, sizeof(struct ops_stats));
+
+ RTE_LCORE_FOREACH_SLAVE(i) {
+ switch (j++) {
+ case 0:
+ rte_eal_remote_launch(test_add_single, NULL, i);
+ break;
+ case 1:
+ rte_eal_remote_launch(test_delete, NULL, i);
+ break;
+ default:
+ rte_eal_remote_launch(test_lookup, NULL, i);
+ }
+ }
+ rte_eal_mp_wait_lcore();
+ }
+ /* There must be at least one attempt of collision (very likely) */
+ while (stats.retries_lookup == 0);
+
+ printf("Retries on lookup op: %u/%u (%.7f%%)\n", stats.retries_lookup,
+ stats.num_lookups, ((double)stats.retries_lookup / stats.num_lookups * 100));
+ printf("Errors on lookup op: %u/%u (%.7f%%)\n", stats.errors_lookup,
+ stats.num_lookups, ((double)stats.errors_lookup / stats.num_lookups * 100));
+ printf("Misses on lookup op: %u/%u (%.7f%%)\n\n", stats.misses_lookup,
+ stats.num_lookups, ((double)stats.misses_lookup / stats.num_lookups * 100));
+ printf("Successes on lookup op: %u/%u (%.7f%%)\n\n", stats.success_lookup,
+ stats.num_lookups, ((double)stats.success_lookup / stats.num_lookups * 100));
+
+ RETURN_IF_ERROR(stats.errors_lookup != 0,
+ "There was at least one error while looking up");
+ return 0;
+
+}
+
+/* All threads (minimum 2) adding entries */
+static int
+test_multi_add(void) {
+ unsigned i;
+
+ do {
+ /* Reset operation stats */
+ rte_tshash_reset(hash_table);
+ memset(&stats, 0, sizeof(struct ops_stats));
+
+ RTE_LCORE_FOREACH_SLAVE(i) {
+ rte_eal_remote_launch(test_add_multi, NULL, i);
+ }
+ rte_eal_mp_wait_lcore();
+ }
+ /* There must be at least one attempt of collision (very likely) */
+ while (stats.retries_add == 0);
+
+ printf("\nRetries on add op: %u/%u (%.7f%%)\n", stats.retries_add,
+ (MAX_KEYS * num_sec_threads),
+ ((double)stats.retries_add / (MAX_KEYS * num_sec_threads) * 100));
+ printf("OOM on add op: %u/%u (%.7f%%)\n", stats.not_enough_mem_add,
+ (MAX_KEYS * num_sec_threads),
+ ((double)stats.not_enough_mem_add / (MAX_KEYS * num_sec_threads) * 100));
+ printf("Already existed on add op: %u/%u (%.7f%%)\n", stats.already_exist_add,
+ (MAX_KEYS * num_sec_threads),
+ ((double)stats.already_exist_add / (MAX_KEYS * num_sec_threads) * 100));
+ printf("No free slots on add op: %u/%u (%.7f%%)\n\n", stats.no_free_slots_add,
+ (MAX_KEYS * num_sec_threads),
+ ((double)stats.no_free_slots_add / (MAX_KEYS * num_sec_threads) * 100));
+ printf("Successes on add op: %u/%u (%.7f%%)\n\n", stats.success_add,
+ (MAX_KEYS * num_sec_threads),
+ ((double)stats.success_add / (MAX_KEYS * num_sec_threads) * 100));
+
+ return 0;
+}
+
+
+static int
+test_tshash_multi_thread(void)
+{
+
+ /* Master + 3 cores are needed (for add/lookup/delete) at least */
+ if (rte_lcore_count() < 4) {
+ printf("At least 4 cores are needed\n");
+ return -1;
+ }
+ num_sec_threads = rte_lcore_count() - 1;
+
+ if (create_hash_table() < 0)
+ return -1;
+
+ if (test_multi_lookup() < 0)
+ return -1;
+
+ if (test_multi_add() < 0)
+ return -1;
+
+ return 0;
+}
+
+static struct test_command tshash_multi_thread_cmd = {
+ .command = "thread_safe_hash_multi_thread_autotest",
+ .callback = test_tshash_multi_thread,
+};
+REGISTER_TEST_COMMAND(tshash_multi_thread_cmd);
diff --git a/app/test/test_tshash_perf.c b/app/test/test_tshash_perf.c
new file mode 100644
index 0000000..79235c1
--- /dev/null
+++ b/app/test/test_tshash_perf.c
@@ -0,0 +1,631 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 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 <stdio.h>
+#include <inttypes.h>
+#include <rte_cycles.h>
+#include <rte_tshash.h>
+#include <rte_random.h>
+#include <rte_string_fns.h>
+#include "test.h"
+
+#define MAX_KEYS (1<<21)
+#define MULTIPLIER 13
+#define MAX_KEYSIZE 128
+#define NUM_KEYSIZES 6
+#define NUM_OPERATIONS 8
+
+#define NUM_LOOKUPS (1<<24)
+#define BURST_SIZE 64
+
+union hash_key {
+ uint64_t hashval;
+ char key[MAX_KEYSIZE];
+};
+
+unsigned key_sizes[] = {16, 32, 48, 64, 96, 128};
+struct rte_tshash *h[NUM_KEYSIZES];
+uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2];
+
+/*
+ * Set up the table so every flow fits in first level bucket
+ */
+static int
+setup_table(unsigned with_hash, unsigned table_index)
+{
+ unsigned i;
+ char name[RTE_TSHASH_NAMESIZE];
+ sprintf(name, "test_phash%d", key_sizes[table_index]);
+
+ h[table_index] = rte_tshash_find_existing(name);
+ if (h[table_index] == NULL) {
+ h[table_index] = rte_tshash_create(name, MAX_KEYS,
+ key_sizes[table_index],
+ rte_socket_id(), NULL);
+ if (h[table_index] == NULL) {
+ printf("Error creating table\n");
+ return -1;
+ } else {
+ }
+ } else
+ rte_tshash_reset(h[table_index]);
+
+ const uint64_t start_tsc = rte_rdtsc();
+
+ for (i = 0; i < MAX_KEYS; i++) {
+ union hash_key k = { .key = {0} };
+ k.hashval = i;
+ if (with_hash) {
+ if (rte_tshash_add_key_with_hash(h[table_index], k.hashval,
+ (const void *)k.key,
+ k.hashval * MULTIPLIER) != 0) {
+ printf("Error adding entry number %u\n", i);
+ return -1;
+ } else
+ continue;
+ } else {
+ if (rte_tshash_add_key(h[table_index], (const void *)k.key,
+ k.hashval * MULTIPLIER != 0)) {
+ printf("Error adding entry number %u\n", i);
+ return -1;
+ }
+ }
+ }
+
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][0][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(h[table_index]);
+ printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+ stats->num_extra_buckets);
+#endif
+
+ printf("\n%"PRIu64" adds in %f seconds\n", (uint64_t)MAX_KEYS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per add\n",
+ cycles[table_index][0][with_hash]);
+ printf("Average %"PRIu64" adds per second\n", (MAX_KEYS * rte_get_tsc_hz())/time_taken);
+
+
+ return 0;
+}
+
+/*
+ * Set up table so half flows are in second level buckets
+ */
+static int
+setup_table_extd(unsigned with_hash, unsigned table_index)
+{
+ unsigned i, j;
+ char name[RTE_TSHASH_NAMESIZE];
+ sprintf(name, "test_phash%d", key_sizes[table_index]);
+
+ h[table_index] = rte_tshash_find_existing(name);
+ if (h[table_index] == NULL) {
+ h[table_index] = rte_tshash_create(name, MAX_KEYS,
+ key_sizes[table_index], rte_socket_id(), NULL);
+ if (h[table_index] == NULL) {
+ printf("Error creating table\n");
+ return -1;
+ } else {
+ }
+ } else
+ rte_tshash_reset(h[table_index]);
+
+ const uint64_t start_tsc = rte_rdtsc();
+ if (with_hash) {
+ for (i = 0; i < MAX_KEYS/8; i++) {
+ for (j = 0; j < 8; j++) {
+ union hash_key k = { .key = {0} };
+ k.hashval = i;
+ k.hashval |= (1lu<<(56+j));
+ k.key[key_sizes[table_index]-1] = j;
+ if (rte_tshash_add_key_with_hash(h[table_index], k.hashval,
+ (const void *)k.key,
+ k.hashval * MULTIPLIER) != 0) {
+ printf("Error adding entry number %u\n", i*7+j);
+ return -1;
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < MAX_KEYS/8; i++) {
+ for (j = 0; j < 8; j++) {
+ union hash_key k = { .key = {0} };
+ k.hashval = i;
+ k.hashval |= (1lu<<(56+j));
+ k.key[key_sizes[table_index]-1] = j;
+ if (rte_tshash_add_key(h[table_index], (const void *)k.key,
+ k.hashval * MULTIPLIER) != 0) {
+ printf("Error adding entry number %u\n", i*7+j);
+ return -1;
+ }
+ }
+ }
+ }
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][1][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(h[table_index]);
+ printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+ stats->num_extra_buckets);
+#endif
+
+ printf("\n%"PRIu64" adds in %f seconds\n", (uint64_t) MAX_KEYS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per add\n",
+ cycles[table_index][1][with_hash]);
+ printf("Average %"PRIu64" adds per second\n",
+ (MAX_KEYS * rte_get_tsc_hz())/time_taken);
+
+ return 0;
+}
+
+
+static int
+timed_lookups(unsigned with_hash, unsigned table_index)
+{
+ uint64_t i;
+ uintptr_t retval;
+ const uint64_t start_tsc = rte_rdtsc();
+
+ for (i = 0; i < NUM_LOOKUPS; i++) {
+ union hash_key k = { .key = {0} };
+
+ k.hashval = rte_rand() % MAX_KEYS;
+ if (with_hash) {
+ if (rte_tshash_lookup_with_hash(h[table_index], k.hashval,
+ (const void *)k.key, &retval) < 0) {
+ printf("Error lookup up hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ } else
+ continue;
+ } else {
+ if (rte_tshash_lookup(h[table_index],
+ (const void *)k.key, &retval) < 0) {
+ printf("Error lookup up hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ }
+ }
+ }
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][2][with_hash] = time_taken/NUM_LOOKUPS;
+
+ printf("%"PRIu64" lookups in %f seconds\n", (uint64_t) NUM_LOOKUPS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per lookup\n",
+ cycles[table_index][2][with_hash]);
+ printf("Average %"PRIu64" lookups per second\n",
+ (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+ return 0;
+}
+
+static int
+timed_lookups_extd(unsigned with_hash, unsigned table_index)
+{
+ uint64_t i;
+ uintptr_t retval;
+ const uint64_t start_tsc = rte_rdtsc();
+ for (i = 0; i < NUM_LOOKUPS; i++) {
+ union hash_key k = { .key = {0} };
+ unsigned tmp = rte_rand() % 8;
+ k.hashval = rte_rand() % MAX_KEYS/8;
+ k.hashval |= (1llu << (tmp+56));
+ k.key[key_sizes[table_index]-1] = tmp;
+ if (with_hash) {
+ if (rte_tshash_lookup_with_hash(h[table_index], k.hashval,
+ (const void *)k.key, &retval) < 0) {
+ printf("Error lookup up hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ } else
+ continue;
+ } else {
+ if (rte_tshash_lookup(h[table_index], (const void *)k.key,
+ &retval) < 0) {
+ printf("Error lookup up hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ }
+ }
+ }
+
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][3][with_hash] = time_taken/NUM_LOOKUPS;
+
+ printf("%"PRIu64" lookups in %f seconds\n", (uint64_t)NUM_LOOKUPS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per lookup\n",
+ cycles[table_index][3][with_hash]);
+ printf("Average %"PRIu64" lookups per second\n",
+ (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+ return 0;
+}
+
+static int
+timed_lookups_multi(unsigned with_hash, unsigned table_index)
+{
+ uint64_t i;
+#if BURST_SIZE == 64
+ const uint64_t lookup_mask = 0xffffffffffffffff;
+#else
+ const uint64_t lookup_mask = (1llu << BURST_SIZE) - 1;
+#endif
+ union hash_key ks[BURST_SIZE] = { { .key = {0} } };
+ const void *keys[BURST_SIZE];
+ uint64_t *h_ptrs[BURST_SIZE];
+
+ for (i = 0; i < BURST_SIZE; i++) {
+ keys[i] = (void *)ks[i].key;
+ h_ptrs[i] = &ks[i].hashval;
+ }
+
+ const uint64_t start_tsc = rte_rdtsc();
+ for (i = 0; i < NUM_LOOKUPS/BURST_SIZE; i++) {
+ unsigned j;
+ uint64_t hashes[BURST_SIZE];
+ uintptr_t retvals[BURST_SIZE];
+ int hitcount;
+ uint64_t hitmask;
+
+ for (j = 0; j < BURST_SIZE; j++) {
+ hashes[j] = rte_rand() % MAX_KEYS;
+ ks[j].hashval = hashes[j];
+ }
+ if (with_hash) {
+ hitcount = rte_tshash_lookup_bulk_with_hash(h[table_index], lookup_mask,
+ h_ptrs, keys, retvals,
+ &hitmask);
+ if (unlikely(hitcount != BURST_SIZE)) {
+ printf("Error lookup up hash keys. Expected retval = %u, got "
+ "%d\n", BURST_SIZE, hitcount);
+ printf("Hit mask = %"PRIx64"\n", hitmask);
+ return -1;
+ } else
+ continue;
+ } else {
+ hitcount = rte_tshash_lookup_bulk(h[table_index], lookup_mask,
+ keys, retvals, &hitmask);
+ if (unlikely(hitcount != BURST_SIZE)) {
+ printf("Error lookup up hash keys. Expected retval = %u, got "
+ "%d\n", BURST_SIZE, hitcount);
+ printf("Hit mask = %"PRIx64"\n", hitmask);
+ return -1;
+ }
+ }
+ }
+
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][4][with_hash] = time_taken/NUM_LOOKUPS;
+
+ printf("%"PRIu64" lookups in %f seconds\n", (uint64_t)NUM_LOOKUPS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per lookup\n",
+ cycles[table_index][4][with_hash]);
+ printf("Average %"PRIu64" lookups per second\n",
+ (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+ return 0;
+}
+
+static int
+timed_lookups_multi_extd(unsigned with_hash, unsigned table_index)
+{
+ uint64_t i;
+#if BURST_SIZE == 64
+ const uint64_t lookup_mask = 0xffffffffffffffff;
+#else
+ const uint64_t lookup_mask = (1llu << BURST_SIZE) - 1;
+#endif
+ union hash_key ks[BURST_SIZE] = { { .key = {0} } };
+ const void *keys[BURST_SIZE];
+ uint64_t *h_ptrs[BURST_SIZE];
+
+ for (i = 0; i < BURST_SIZE; i++) {
+ keys[i] = (void *)ks[i].key;
+ h_ptrs[i] = &ks[i].hashval;
+ }
+
+ const uint64_t start_tsc = rte_rdtsc();
+ for (i = 0; i < NUM_LOOKUPS/BURST_SIZE; i++) {
+ unsigned j;
+ uint64_t hashes[BURST_SIZE];
+ uintptr_t retvals[BURST_SIZE];
+ int hitcount;
+ uint64_t hitmask;
+
+ for (j = 0; j < BURST_SIZE; j++) {
+ unsigned tmp = rte_rand() % 8;
+ hashes[j] = rte_rand() % MAX_KEYS/8;
+ hashes[j] |= (1lu << (56 + tmp));
+ ks[j].hashval = hashes[j];
+ ks[j].key[key_sizes[table_index] - 1] = tmp;
+ }
+ if (with_hash) {
+ hitcount = rte_tshash_lookup_bulk_with_hash(h[table_index], lookup_mask,
+ h_ptrs, keys, retvals,
+ &hitmask);
+ if (unlikely(hitcount != BURST_SIZE)) {
+ printf("Error lookup up hash keys. Expected retval = %u, got "
+ "%d\n", BURST_SIZE, hitcount);
+ printf("Hit mask = %"PRIx64"\n", hitmask);
+ return -1;
+ } else
+ continue;
+ } else {
+ hitcount = rte_tshash_lookup_bulk(h[table_index], lookup_mask,
+ keys, retvals, &hitmask);
+ if (unlikely(hitcount != BURST_SIZE)) {
+ printf("Error lookup up hash keys. Expected retval = %u, got "
+ "%d\n", BURST_SIZE, hitcount);
+ printf("Hit mask = %"PRIx64"\n", hitmask);
+ return -1;
+ }
+ }
+ }
+
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][5][with_hash] = time_taken/NUM_LOOKUPS;
+
+ printf("%"PRIu64" lookups in %f seconds\n", (uint64_t) NUM_LOOKUPS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per lookup\n",
+ cycles[table_index][5][with_hash]);
+ printf("Average %"PRIu64" lookups per second\n",
+ (NUM_LOOKUPS * rte_get_tsc_hz())/time_taken);
+ return 0;
+}
+
+static int
+timed_deletes(unsigned with_hash, unsigned table_index)
+{
+ uint64_t i;
+ const uint64_t start_tsc = rte_rdtsc();
+ for (i = 0; i < MAX_KEYS; i++) {
+ union hash_key k = { .key = {0} };
+
+ k.hashval = i;
+ if (with_hash) {
+ if (rte_tshash_del_key_with_hash(h[table_index], k.hashval,
+ (const void *)k.key, NULL) < 0) {
+ printf("Error deleting hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ } else
+ continue;
+ } else {
+ if (rte_tshash_del_key(h[table_index],
+ (const void *)k.key, NULL) < 0) {
+ printf("Error deleting hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ }
+ }
+
+ }
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][6][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(h[table_index]);
+ printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+ stats->num_extra_buckets);
+#endif
+
+ printf("\n%"PRIu64" deletions in %f seconds\n", (uint64_t) MAX_KEYS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per deletion\n",
+ cycles[table_index][6][with_hash]);
+ printf("Average %"PRIu64" deletions per second\n",
+ (MAX_KEYS * rte_get_tsc_hz())/time_taken);
+
+ return 0;
+}
+
+static int
+timed_deletes_extd(unsigned with_hash, unsigned table_index)
+{
+ uint64_t i, j;
+
+ const uint64_t start_tsc = rte_rdtsc();
+ for (i = 0; i < MAX_KEYS/8; i++) {
+ for (j = 0; j < 8; j++) {
+ union hash_key k = { .key = {0} };
+ k.hashval = i;
+ k.hashval |= (1lu<<(56+j));
+ k.key[key_sizes[table_index]-1] = j;
+ if (with_hash) {
+ if (rte_tshash_del_key_with_hash(h[table_index], k.hashval,
+ (const void *)k.key, NULL)
+ != 0) {
+ printf("Error deleting hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ } else
+ continue;
+ } else {
+ if (rte_tshash_del_key(h[table_index], (const void *)k.key,
+ NULL) != 0) {
+ printf("Error deleting hash key %"PRIu64"\n", k.hashval);
+ return -1;
+ }
+ }
+ }
+ }
+ const uint64_t end_tsc = rte_rdtsc();
+ const uint64_t time_taken = end_tsc - start_tsc;
+ const float seconds_taken = (float)time_taken/rte_get_tsc_hz();
+ cycles[table_index][7][with_hash] = time_taken/MAX_KEYS;
+
+#ifdef RTE_LIBRTE_THREAD_SAFE_HASH_STATS
+ const struct rte_tshash_stats *stats = rte_tshash_get_stats(h[table_index]);
+ printf("used_slots = %u, extra_buckets = %u\n", stats->used_slots,
+ stats->num_extra_buckets);
+#endif
+
+ printf("\n%"PRIu64" deletions in %f seconds\n", (uint64_t)MAX_KEYS,
+ seconds_taken);
+ printf("Average %"PRIu64" tsc ticks per deletion\n",
+ cycles[table_index][7][with_hash]);
+ printf("Average %"PRIu64" deletions per second\n", (MAX_KEYS * rte_get_tsc_hz())/time_taken);
+
+ return 0;
+}
+
+static int
+reset_table(unsigned table_index) {
+
+ rte_tshash_reset(h[table_index]);
+ return 0;
+}
+
+static int
+test_tshash_perf(void)
+{
+ unsigned i, j;
+
+ for (i = 0; i < NUM_KEYSIZES; i++) {
+ printf("\n------ KEY SIZE = %u ----------\n\n", key_sizes[i]);
+ printf("\n ----- WITH PRECALCULATED HASH VALUES -----\n\n");
+ printf("Table with only first-level buckets used\n");
+ if (setup_table(1, i) < 0)
+ return -1;
+
+ printf("\nTimed lookups\n");
+ printf("------------------\n");
+ if (timed_lookups(1, i) < 0)
+ return -1;
+
+ printf("\nTimed lookups multi \n");
+ printf("------------------\n");
+ if (timed_lookups_multi(1, i) < 0)
+ return -1;
+
+ printf("\nTimed deletions \n");
+ printf("------------------\n");
+ if (timed_deletes(1, i) < 0)
+ return -1;
+
+ printf("\nResetting table to use extra buckets\n");
+ if (setup_table_extd(1, i) < 0)
+ return -1;
+
+ printf("\nTimed lookups extd\n");
+ printf("------------------\n");
+ if (timed_lookups_extd(1, i) < 0)
+ return -1;
+
+ printf("\nTimed lookups multi extd\n");
+ printf("------------------\n");
+ if (timed_lookups_multi_extd(1, i) < 0)
+ return -1;
+
+ printf("\nTimed deletions extd\n");
+ printf("------------------\n");
+ if (timed_deletes_extd(1, i) < 0)
+ return -1;
+
+ reset_table(i);
+
+ printf("\n ----- WITH JUST KEYS -----\n\n");
+
+ printf("Table with only first-level buckets used\n");
+ if (setup_table(0, i) < 0)
+ return -1;
+
+ printf("\nTimed lookups\n");
+ printf("------------------\n");
+ if (timed_lookups(0, i) < 0)
+ return -1;
+
+ printf("\nTimed lookups multi \n");
+ printf("------------------\n");
+ if (timed_lookups_multi(0, i) < 0)
+ return -1;
+
+ printf("\nTimed deletions \n");
+ printf("------------------\n");
+ if (timed_deletes(0, i) < 0)
+ return -1;
+
+ reset_table(i);
+
+ }
+ printf("\nResults (in CPU cycles/operation)\n");
+ printf("-----------------------------------\n");
+ printf("\nWith just keys (only 1st level buckets)\n");
+ printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+ "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+ for (i = 0; i < NUM_KEYSIZES; i++) {
+ printf("%-18d", key_sizes[i]);
+ for (j = 0; j < 8; j = j+2)
+ printf("%-18"PRIu64, cycles[i][j][0]);
+ printf("\n");
+ }
+
+ printf("\nWith precalculated hash (only 1st level buckets)\n");
+ printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+ "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+ for (i = 0; i < NUM_KEYSIZES; i++) {
+ printf("%-18d", key_sizes[i]);
+ for (j = 0; j < 8; j = j+2)
+ printf("%-18"PRIu64, cycles[i][j][1]);
+ printf("\n");
+ }
+ printf("\nWith precalculated hash (with extended buckets)\n");
+ printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+ "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+ for (i = 0; i < NUM_KEYSIZES; i++) {
+ printf("%-18d", key_sizes[i]);
+ for (j = 1; j < 8; j = j+2)
+ printf("%-18"PRIu64, cycles[i][j][1]);
+ printf("\n");
+ }
+ return 0;
+}
+
+static struct test_command tshash_perf_cmd = {
+ .command = "thread_safe_hash_perf_autotest",
+ .callback = test_tshash_perf,
+};
+REGISTER_TEST_COMMAND(tshash_perf_cmd);
--
1.7.7.6
next prev parent reply other threads:[~2014-09-18 10:29 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-09-18 10:34 [dpdk-dev] [PATCH 0/3] New Thread Safe Hash Library Pablo de Lara
2014-09-18 10:34 ` [dpdk-dev] [PATCH 1/3] eal: add const in prefetch functions Pablo de Lara
2014-09-18 10:34 ` [dpdk-dev] [PATCH 2/3] lib/librte_tshash: New Thread Safe Hash library for DPDK Pablo de Lara
2014-09-18 10:34 ` Pablo de Lara [this message]
2014-09-18 12:21 ` [dpdk-dev] [PATCH 0/3] New Thread Safe Hash Library Neil Horman
2014-09-18 15:31 ` De Lara Guarch, Pablo
2014-09-18 15:45 ` Thomas Monjalon
2014-09-18 16:09 ` Neil Horman
2014-09-18 22:30 ` Stephen Hemminger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1411036471-3822-4-git-send-email-pablo.de.lara.guarch@intel.com \
--to=pablo.de.lara.guarch@intel.com \
--cc=dev@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).