1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2016-2017 Intel Corporation 3 */ 4 5 #include <stdio.h> 6 #include <inttypes.h> 7 8 #include <rte_lcore.h> 9 #include <rte_cycles.h> 10 #include <rte_malloc.h> 11 #include <rte_random.h> 12 #include <rte_efd.h> 13 #include <rte_memcpy.h> 14 #include <rte_thash.h> 15 16 #include "test.h" 17 18 #define NUM_KEYSIZES 10 19 #define NUM_SHUFFLES 10 20 #define MAX_KEYSIZE 64 21 #define MAX_ENTRIES (1 << 19) 22 #define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */ 23 #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */ 24 25 #if RTE_EFD_VALUE_NUM_BITS == 32 26 #define VALUE_BITMASK 0xffffffff 27 #else 28 #define VALUE_BITMASK ((1 << RTE_EFD_VALUE_NUM_BITS) - 1) 29 #endif 30 static unsigned int test_socket_id; 31 32 static inline uint8_t efd_get_all_sockets_bitmask(void) 33 { 34 uint8_t all_cpu_sockets_bitmask = 0; 35 unsigned int i; 36 unsigned int next_lcore = rte_get_main_lcore(); 37 const int val_true = 1, val_false = 0; 38 for (i = 0; i < rte_lcore_count(); i++) { 39 all_cpu_sockets_bitmask |= 1 << rte_lcore_to_socket_id(next_lcore); 40 next_lcore = rte_get_next_lcore(next_lcore, val_false, val_true); 41 } 42 43 return all_cpu_sockets_bitmask; 44 } 45 46 enum operations { 47 ADD = 0, 48 LOOKUP, 49 LOOKUP_MULTI, 50 DELETE, 51 NUM_OPERATIONS 52 }; 53 54 struct efd_perf_params { 55 struct rte_efd_table *efd_table; 56 uint32_t key_size; 57 unsigned int cycle; 58 }; 59 60 static uint32_t hashtest_key_lens[] = { 61 /* standard key sizes */ 62 4, 8, 16, 32, 48, 64, 63 /* IPv4 SRC + DST + protocol, unpadded */ 64 9, 65 /* IPv4 5-tuple, unpadded */ 66 13, 67 /* IPv6 5-tuple, unpadded */ 68 37, 69 /* IPv6 5-tuple, padded to 8-byte boundary */ 70 40 71 }; 72 73 /* Array to store number of cycles per operation */ 74 static uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS]; 75 76 /* Array to store the data */ 77 static efd_value_t data[KEYS_TO_ADD]; 78 79 /* Array to store all input keys */ 80 static uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE]; 81 82 /* Shuffle the keys that have been added, so lookups will be totally random */ 83 static void 84 shuffle_input_keys(struct efd_perf_params *params) 85 { 86 efd_value_t temp_data; 87 unsigned int i; 88 uint32_t swap_idx; 89 uint8_t temp_key[MAX_KEYSIZE]; 90 91 for (i = KEYS_TO_ADD - 1; i > 0; i--) { 92 swap_idx = rte_rand() % i; 93 94 memcpy(temp_key, keys[i], hashtest_key_lens[params->cycle]); 95 temp_data = data[i]; 96 97 memcpy(keys[i], keys[swap_idx], hashtest_key_lens[params->cycle]); 98 data[i] = data[swap_idx]; 99 100 memcpy(keys[swap_idx], temp_key, hashtest_key_lens[params->cycle]); 101 data[swap_idx] = temp_data; 102 } 103 } 104 105 static int key_compare(const void *key1, const void *key2) 106 { 107 return memcmp(key1, key2, MAX_KEYSIZE); 108 } 109 110 /* 111 * TODO: we could "error proof" these as done in test_hash_perf.c ln 165: 112 * 113 * The current setup may give errors if too full in some cases which we check 114 * for. However, since EFD allows for ~99% capacity, these errors are rare for 115 * #"KEYS_TO_ADD" which is 75% capacity. 116 */ 117 static int 118 setup_keys_and_data(struct efd_perf_params *params, unsigned int cycle) 119 { 120 unsigned int i, j; 121 int num_duplicates; 122 123 params->key_size = hashtest_key_lens[cycle]; 124 params->cycle = cycle; 125 126 /* Reset all arrays */ 127 for (i = 0; i < params->key_size; i++) 128 keys[0][i] = 0; 129 130 /* Generate a list of keys, some of which may be duplicates */ 131 for (i = 0; i < KEYS_TO_ADD; i++) { 132 for (j = 0; j < params->key_size; j++) 133 keys[i][j] = rte_rand() & 0xFF; 134 135 data[i] = rte_rand() & VALUE_BITMASK; 136 } 137 138 /* Remove duplicates from the keys array */ 139 do { 140 num_duplicates = 0; 141 142 /* Sort the list of keys to make it easier to find duplicates */ 143 qsort(keys, KEYS_TO_ADD, MAX_KEYSIZE, key_compare); 144 145 /* Sift through the list of keys and look for duplicates */ 146 int num_duplicates = 0; 147 for (i = 0; i < KEYS_TO_ADD - 1; i++) { 148 if (memcmp(keys[i], keys[i + 1], params->key_size) == 0) { 149 /* This key already exists, try again */ 150 num_duplicates++; 151 for (j = 0; j < params->key_size; j++) 152 keys[i][j] = rte_rand() & 0xFF; 153 } 154 } 155 } while (num_duplicates != 0); 156 157 /* Shuffle the random values again */ 158 shuffle_input_keys(params); 159 160 params->efd_table = rte_efd_create("test_efd_perf", 161 MAX_ENTRIES, params->key_size, 162 efd_get_all_sockets_bitmask(), test_socket_id); 163 TEST_ASSERT_NOT_NULL(params->efd_table, "Error creating the efd table\n"); 164 165 return 0; 166 } 167 168 static int 169 timed_adds(struct efd_perf_params *params) 170 { 171 const uint64_t start_tsc = rte_rdtsc(); 172 unsigned int i, a; 173 int32_t ret; 174 175 for (i = 0; i < KEYS_TO_ADD; i++) { 176 ret = rte_efd_update(params->efd_table, test_socket_id, keys[i], 177 data[i]); 178 if (ret != 0) { 179 printf("Error %d in rte_efd_update - key=0x", ret); 180 for (a = 0; a < params->key_size; a++) 181 printf("%02x", keys[i][a]); 182 printf(" value=%d\n", data[i]); 183 184 return -1; 185 } 186 } 187 188 const uint64_t end_tsc = rte_rdtsc(); 189 const uint64_t time_taken = end_tsc - start_tsc; 190 191 cycles[params->cycle][ADD] = time_taken / KEYS_TO_ADD; 192 return 0; 193 } 194 195 static int 196 timed_lookups(struct efd_perf_params *params) 197 { 198 unsigned int i, j, a; 199 const uint64_t start_tsc = rte_rdtsc(); 200 efd_value_t ret_data; 201 202 for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) { 203 for (j = 0; j < KEYS_TO_ADD; j++) { 204 ret_data = rte_efd_lookup(params->efd_table, 205 test_socket_id, keys[j]); 206 if (ret_data != data[j]) { 207 printf("Value mismatch using rte_efd_lookup: " 208 "key #%d (0x", i); 209 for (a = 0; a < params->key_size; a++) 210 printf("%02x", keys[i][a]); 211 printf(")\n"); 212 printf(" Expected %d, got %d\n", data[i], 213 ret_data); 214 215 return -1; 216 } 217 218 } 219 } 220 221 const uint64_t end_tsc = rte_rdtsc(); 222 const uint64_t time_taken = end_tsc - start_tsc; 223 224 cycles[params->cycle][LOOKUP] = time_taken / NUM_LOOKUPS; 225 226 return 0; 227 } 228 229 static int 230 timed_lookups_multi(struct efd_perf_params *params) 231 { 232 unsigned int i, j, k, a; 233 efd_value_t result[RTE_EFD_BURST_MAX] = {0}; 234 const void *keys_burst[RTE_EFD_BURST_MAX]; 235 const uint64_t start_tsc = rte_rdtsc(); 236 237 for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) { 238 for (j = 0; j < KEYS_TO_ADD / RTE_EFD_BURST_MAX; j++) { 239 for (k = 0; k < RTE_EFD_BURST_MAX; k++) 240 keys_burst[k] = keys[j * RTE_EFD_BURST_MAX + k]; 241 242 rte_efd_lookup_bulk(params->efd_table, test_socket_id, 243 RTE_EFD_BURST_MAX, 244 keys_burst, result); 245 246 for (k = 0; k < RTE_EFD_BURST_MAX; k++) { 247 uint32_t data_idx = j * RTE_EFD_BURST_MAX + k; 248 if (result[k] != data[data_idx]) { 249 printf("Value mismatch using " 250 "rte_efd_lookup_bulk: key #%d " 251 "(0x", i); 252 for (a = 0; a < params->key_size; a++) 253 printf("%02x", 254 keys[data_idx][a]); 255 printf(")\n"); 256 printf(" Expected %d, got %d\n", 257 data[data_idx], result[k]); 258 259 return -1; 260 } 261 } 262 } 263 } 264 265 const uint64_t end_tsc = rte_rdtsc(); 266 const uint64_t time_taken = end_tsc - start_tsc; 267 268 cycles[params->cycle][LOOKUP_MULTI] = time_taken / NUM_LOOKUPS; 269 270 return 0; 271 } 272 273 static int 274 timed_deletes(struct efd_perf_params *params) 275 { 276 unsigned int i, a; 277 const uint64_t start_tsc = rte_rdtsc(); 278 int32_t ret; 279 280 for (i = 0; i < KEYS_TO_ADD; i++) { 281 ret = rte_efd_delete(params->efd_table, test_socket_id, keys[i], 282 NULL); 283 284 if (ret != 0) { 285 printf("Error %d in rte_efd_delete - key=0x", ret); 286 for (a = 0; a < params->key_size; a++) 287 printf("%02x", keys[i][a]); 288 printf("\n"); 289 290 return -1; 291 } 292 } 293 294 const uint64_t end_tsc = rte_rdtsc(); 295 const uint64_t time_taken = end_tsc - start_tsc; 296 297 cycles[params->cycle][DELETE] = time_taken / KEYS_TO_ADD; 298 299 return 0; 300 } 301 302 static void 303 perform_frees(struct efd_perf_params *params) 304 { 305 if (params->efd_table != NULL) { 306 rte_efd_free(params->efd_table); 307 params->efd_table = NULL; 308 } 309 } 310 311 static int 312 exit_with_fail(const char *testname, struct efd_perf_params *params, 313 unsigned int i) 314 { 315 316 printf("<<<<<Test %s failed at keysize %d iteration %d >>>>>\n", 317 testname, hashtest_key_lens[params->cycle], i); 318 perform_frees(params); 319 return -1; 320 } 321 322 static int 323 run_all_tbl_perf_tests(void) 324 { 325 unsigned int i, j; 326 struct efd_perf_params params; 327 328 printf("Measuring performance, please wait\n"); 329 fflush(stdout); 330 331 test_socket_id = rte_socket_id(); 332 333 for (i = 0; i < NUM_KEYSIZES; i++) { 334 335 if (setup_keys_and_data(¶ms, i) < 0) { 336 printf("Could not create keys/data/table\n"); 337 return -1; 338 } 339 340 if (timed_adds(¶ms) < 0) 341 return exit_with_fail("timed_adds", ¶ms, i); 342 343 for (j = 0; j < NUM_SHUFFLES; j++) 344 shuffle_input_keys(¶ms); 345 346 if (timed_lookups(¶ms) < 0) 347 return exit_with_fail("timed_lookups", ¶ms, i); 348 349 if (timed_lookups_multi(¶ms) < 0) 350 return exit_with_fail("timed_lookups_multi", ¶ms, i); 351 352 if (timed_deletes(¶ms) < 0) 353 return exit_with_fail("timed_deletes", ¶ms, i); 354 355 /* Print a dot to show progress on operations */ 356 printf("."); 357 fflush(stdout); 358 359 perform_frees(¶ms); 360 } 361 362 printf("\nResults (in CPU cycles/operation)\n"); 363 printf("-----------------------------------\n"); 364 printf("\n%-18s%-18s%-18s%-18s%-18s\n", 365 "Keysize", "Add", "Lookup", "Lookup_bulk", "Delete"); 366 for (i = 0; i < NUM_KEYSIZES; i++) { 367 printf("%-18d", hashtest_key_lens[i]); 368 for (j = 0; j < NUM_OPERATIONS; j++) 369 printf("%-18"PRIu64, cycles[i][j]); 370 printf("\n"); 371 } 372 return 0; 373 } 374 375 static int 376 test_efd_perf(void) 377 { 378 379 if (run_all_tbl_perf_tests() < 0) 380 return -1; 381 382 return 0; 383 } 384 385 REGISTER_TEST_COMMAND(efd_perf_autotest, test_efd_perf); 386