/*- * BSD LICENSE * * Copyright(c) 2016-2017 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 "test.h" #define NUM_KEYSIZES 10 #define NUM_SHUFFLES 10 #define MAX_KEYSIZE 64 #define MAX_ENTRIES (1 << 19) #define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */ #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */ #if RTE_EFD_VALUE_NUM_BITS == 32 #define VALUE_BITMASK 0xffffffff #else #define VALUE_BITMASK ((1 << RTE_EFD_VALUE_NUM_BITS) - 1) #endif static unsigned int test_socket_id; static inline uint8_t efd_get_all_sockets_bitmask(void) { uint8_t all_cpu_sockets_bitmask = 0; unsigned int i; unsigned int next_lcore = rte_get_master_lcore(); const int val_true = 1, val_false = 0; for (i = 0; i < rte_lcore_count(); i++) { all_cpu_sockets_bitmask |= 1 << rte_lcore_to_socket_id(next_lcore); next_lcore = rte_get_next_lcore(next_lcore, val_false, val_true); } return all_cpu_sockets_bitmask; } enum operations { ADD = 0, LOOKUP, LOOKUP_MULTI, DELETE, NUM_OPERATIONS }; struct efd_perf_params { struct rte_efd_table *efd_table; uint32_t key_size; unsigned int cycle; }; static uint32_t hashtest_key_lens[] = { /* standard key sizes */ 4, 8, 16, 32, 48, 64, /* IPv4 SRC + DST + protocol, unpadded */ 9, /* IPv4 5-tuple, unpadded */ 13, /* IPv6 5-tuple, unpadded */ 37, /* IPv6 5-tuple, padded to 8-byte boundary */ 40 }; /* Array to store number of cycles per operation */ uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS]; /* Array to store the data */ efd_value_t data[KEYS_TO_ADD]; /* Array to store all input keys */ uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE]; /* Shuffle the keys that have been added, so lookups will be totally random */ static void shuffle_input_keys(struct efd_perf_params *params) { efd_value_t temp_data; unsigned int i; uint32_t swap_idx; uint8_t temp_key[MAX_KEYSIZE]; for (i = KEYS_TO_ADD - 1; i > 0; i--) { swap_idx = rte_rand() % i; memcpy(temp_key, keys[i], hashtest_key_lens[params->cycle]); temp_data = data[i]; memcpy(keys[i], keys[swap_idx], hashtest_key_lens[params->cycle]); data[i] = data[swap_idx]; memcpy(keys[swap_idx], temp_key, hashtest_key_lens[params->cycle]); data[swap_idx] = temp_data; } } static int key_compare(const void *key1, const void *key2) { return memcmp(key1, key2, MAX_KEYSIZE); } /* * TODO: we could "error proof" these as done in test_hash_perf.c ln 165: * * The current setup may give errors if too full in some cases which we check * for. However, since EFD allows for ~99% capacity, these errors are rare for * #"KEYS_TO_ADD" which is 75% capacity. */ static int setup_keys_and_data(struct efd_perf_params *params, unsigned int cycle) { unsigned int i, j; int num_duplicates; params->key_size = hashtest_key_lens[cycle]; params->cycle = cycle; /* Reset all arrays */ for (i = 0; i < params->key_size; i++) keys[0][i] = 0; /* Generate a list of keys, some of which may be duplicates */ for (i = 0; i < KEYS_TO_ADD; i++) { for (j = 0; j < params->key_size; j++) keys[i][j] = rte_rand() & 0xFF; data[i] = rte_rand() & VALUE_BITMASK; } /* Remove duplicates from the keys array */ do { num_duplicates = 0; /* Sort the list of keys to make it easier to find duplicates */ qsort(keys, KEYS_TO_ADD, MAX_KEYSIZE, key_compare); /* Sift through the list of keys and look for duplicates */ int num_duplicates = 0; for (i = 0; i < KEYS_TO_ADD - 1; i++) { if (memcmp(keys[i], keys[i + 1], params->key_size) == 0) { /* This key already exists, try again */ num_duplicates++; for (j = 0; j < params->key_size; j++) keys[i][j] = rte_rand() & 0xFF; } } } while (num_duplicates != 0); /* Shuffle the random values again */ shuffle_input_keys(params); params->efd_table = rte_efd_create("test_efd_perf", MAX_ENTRIES, params->key_size, efd_get_all_sockets_bitmask(), test_socket_id); TEST_ASSERT_NOT_NULL(params->efd_table, "Error creating the efd table\n"); return 0; } static int timed_adds(struct efd_perf_params *params) { const uint64_t start_tsc = rte_rdtsc(); unsigned int i, a; int32_t ret; for (i = 0; i < KEYS_TO_ADD; i++) { ret = rte_efd_update(params->efd_table, test_socket_id, keys[i], data[i]); if (ret != 0) { printf("Error %d in rte_efd_update - key=0x", ret); for (a = 0; a < params->key_size; a++) printf("%02x", keys[i][a]); printf(" value=%d\n", data[i]); return -1; } } const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; cycles[params->cycle][ADD] = time_taken / KEYS_TO_ADD; return 0; } static int timed_lookups(struct efd_perf_params *params) { unsigned int i, j, a; const uint64_t start_tsc = rte_rdtsc(); efd_value_t ret_data; for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) { for (j = 0; j < KEYS_TO_ADD; j++) { ret_data = rte_efd_lookup(params->efd_table, test_socket_id, keys[j]); if (ret_data != data[j]) { printf("Value mismatch using rte_efd_lookup: " "key #%d (0x", i); for (a = 0; a < params->key_size; a++) printf("%02x", keys[i][a]); printf(")\n"); printf(" Expected %d, got %d\n", data[i], ret_data); return -1; } } } const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; cycles[params->cycle][LOOKUP] = time_taken / NUM_LOOKUPS; return 0; } static int timed_lookups_multi(struct efd_perf_params *params) { unsigned int i, j, k, a; efd_value_t result[RTE_EFD_BURST_MAX] = {0}; const void *keys_burst[RTE_EFD_BURST_MAX]; const uint64_t start_tsc = rte_rdtsc(); for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) { for (j = 0; j < KEYS_TO_ADD / RTE_EFD_BURST_MAX; j++) { for (k = 0; k < RTE_EFD_BURST_MAX; k++) keys_burst[k] = keys[j * RTE_EFD_BURST_MAX + k]; rte_efd_lookup_bulk(params->efd_table, test_socket_id, RTE_EFD_BURST_MAX, keys_burst, result); for (k = 0; k < RTE_EFD_BURST_MAX; k++) { uint32_t data_idx = j * RTE_EFD_BURST_MAX + k; if (result[k] != data[data_idx]) { printf("Value mismatch using " "rte_efd_lookup_bulk: key #%d " "(0x", i); for (a = 0; a < params->key_size; a++) printf("%02x", keys[data_idx][a]); printf(")\n"); printf(" Expected %d, got %d\n", data[data_idx], result[k]); return -1; } } } } const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; cycles[params->cycle][LOOKUP_MULTI] = time_taken / NUM_LOOKUPS; return 0; } static int timed_deletes(struct efd_perf_params *params) { unsigned int i, a; const uint64_t start_tsc = rte_rdtsc(); int32_t ret; for (i = 0; i < KEYS_TO_ADD; i++) { ret = rte_efd_delete(params->efd_table, test_socket_id, keys[i], NULL); if (ret != 0) { printf("Error %d in rte_efd_delete - key=0x", ret); for (a = 0; a < params->key_size; a++) printf("%02x", keys[i][a]); printf("\n"); return -1; } } const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; cycles[params->cycle][DELETE] = time_taken / KEYS_TO_ADD; return 0; } static void perform_frees(struct efd_perf_params *params) { if (params->efd_table != NULL) { rte_efd_free(params->efd_table); params->efd_table = NULL; } } static int exit_with_fail(const char *testname, struct efd_perf_params *params, unsigned int i) { printf("<<<<>>>>\n", testname, hashtest_key_lens[params->cycle], i); perform_frees(params); return -1; } static int run_all_tbl_perf_tests(void) { unsigned int i, j; struct efd_perf_params params; printf("Measuring performance, please wait\n"); fflush(stdout); test_socket_id = rte_socket_id(); for (i = 0; i < NUM_KEYSIZES; i++) { if (setup_keys_and_data(¶ms, i) < 0) { printf("Could not create keys/data/table\n"); return -1; } if (timed_adds(¶ms) < 0) return exit_with_fail("timed_adds", ¶ms, i); for (j = 0; j < NUM_SHUFFLES; j++) shuffle_input_keys(¶ms); if (timed_lookups(¶ms) < 0) return exit_with_fail("timed_lookups", ¶ms, i); if (timed_lookups_multi(¶ms) < 0) return exit_with_fail("timed_lookups_multi", ¶ms, i); if (timed_deletes(¶ms) < 0) return exit_with_fail("timed_deletes", ¶ms, i); /* Print a dot to show progress on operations */ printf("."); fflush(stdout); perform_frees(¶ms); } printf("\nResults (in CPU cycles/operation)\n"); printf("-----------------------------------\n"); printf("\n%-18s%-18s%-18s%-18s%-18s\n", "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete"); for (i = 0; i < NUM_KEYSIZES; i++) { printf("%-18d", hashtest_key_lens[i]); for (j = 0; j < NUM_OPERATIONS; j++) printf("%-18"PRIu64, cycles[i][j]); printf("\n"); } return 0; } static int test_efd_perf(void) { if (run_all_tbl_perf_tests() < 0) return -1; return 0; } REGISTER_TEST_COMMAND(efd_perf_autotest, test_efd_perf);