xref: /dpdk/app/test/test_hash_perf.c (revision 97b914f4e715565d53d38ac6e04815b9be5e58a9)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2015 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_hash.h>
12 #include <rte_hash_crc.h>
13 #include <rte_jhash.h>
14 #include <rte_fbk_hash.h>
15 #include <rte_random.h>
16 #include <rte_string_fns.h>
17 
18 #include "test.h"
19 
20 #define MAX_ENTRIES (1 << 19)
21 #define KEYS_TO_ADD (MAX_ENTRIES)
22 #define ADD_PERCENT 0.75 /* 75% table utilization */
23 #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */
24 /* BUCKET_SIZE should be same as RTE_HASH_BUCKET_ENTRIES in rte_hash library */
25 #define BUCKET_SIZE 8
26 #define NUM_BUCKETS (MAX_ENTRIES / BUCKET_SIZE)
27 #define MAX_KEYSIZE 64
28 #define NUM_KEYSIZES 10
29 #define NUM_SHUFFLES 10
30 #define BURST_SIZE 16
31 
32 enum operations {
33 	OP_ADD = 0,
34 	OP_LOOKUP,
35 	OP_LOOKUP_MULTI,
36 	OP_DELETE,
37 	NUM_OPERATIONS
38 };
39 
40 static uint32_t hashtest_key_lens[] = {
41 	/* standard key sizes */
42 	4, 8, 16, 32, 48, 64,
43 	/* IPv4 SRC + DST + protocol, unpadded */
44 	9,
45 	/* IPv4 5-tuple, unpadded */
46 	13,
47 	/* IPv6 5-tuple, unpadded */
48 	37,
49 	/* IPv6 5-tuple, padded to 8-byte boundary */
50 	40
51 };
52 
53 struct rte_hash *h[NUM_KEYSIZES];
54 
55 /* Array that stores if a slot is full */
56 static uint8_t slot_taken[MAX_ENTRIES];
57 
58 /* Array to store number of cycles per operation */
59 static uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS][2][2];
60 
61 /* Array to store all input keys */
62 static uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
63 
64 /* Array to store the precomputed hash for 'keys' */
65 static hash_sig_t signatures[KEYS_TO_ADD];
66 
67 /* Array to store how many busy entries have each bucket */
68 static uint8_t buckets[NUM_BUCKETS];
69 
70 /* Array to store the positions where keys are added */
71 static int32_t positions[KEYS_TO_ADD];
72 
73 /* Parameters used for hash table in unit test functions. */
74 static struct rte_hash_parameters ut_params = {
75 	.entries = MAX_ENTRIES,
76 	.hash_func = rte_jhash,
77 	.hash_func_init_val = 0,
78 };
79 
80 static int
81 create_table(unsigned int with_data, unsigned int table_index,
82 		unsigned int with_locks, unsigned int ext)
83 {
84 	char name[RTE_HASH_NAMESIZE];
85 
86 	if (with_data)
87 		/* Table will store 8-byte data */
88 		snprintf(name, sizeof(name), "test_hash%u_data",
89 				hashtest_key_lens[table_index]);
90 	else
91 		snprintf(name, sizeof(name), "test_hash%u",
92 				hashtest_key_lens[table_index]);
93 
94 
95 	if (with_locks)
96 		ut_params.extra_flag =
97 			RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT
98 				| RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
99 	else
100 		ut_params.extra_flag = 0;
101 
102 	if (ext)
103 		ut_params.extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE;
104 
105 	ut_params.name = name;
106 	ut_params.key_len = hashtest_key_lens[table_index];
107 	ut_params.socket_id = rte_socket_id();
108 	h[table_index] = rte_hash_find_existing(name);
109 	rte_hash_free(h[table_index]);
110 	h[table_index] = rte_hash_create(&ut_params);
111 	if (h[table_index] == NULL) {
112 		printf("Error creating table\n");
113 		return -1;
114 	}
115 	return 0;
116 
117 }
118 
119 /* Shuffle the keys that have been added, so lookups will be totally random */
120 static void
121 shuffle_input_keys(unsigned int table_index, unsigned int ext)
122 {
123 	unsigned i;
124 	uint32_t swap_idx;
125 	uint8_t temp_key[MAX_KEYSIZE];
126 	hash_sig_t temp_signature;
127 	int32_t temp_position;
128 	unsigned int keys_to_add;
129 
130 	if (!ext)
131 		keys_to_add = KEYS_TO_ADD * ADD_PERCENT;
132 	else
133 		keys_to_add = KEYS_TO_ADD;
134 
135 	for (i = keys_to_add - 1; i > 0; i--) {
136 		swap_idx = rte_rand() % i;
137 
138 		memcpy(temp_key, keys[i], hashtest_key_lens[table_index]);
139 		temp_signature = signatures[i];
140 		temp_position = positions[i];
141 
142 		memcpy(keys[i], keys[swap_idx], hashtest_key_lens[table_index]);
143 		signatures[i] = signatures[swap_idx];
144 		positions[i] = positions[swap_idx];
145 
146 		memcpy(keys[swap_idx], temp_key, hashtest_key_lens[table_index]);
147 		signatures[swap_idx] = temp_signature;
148 		positions[swap_idx] = temp_position;
149 	}
150 }
151 
152 /*
153  * Looks for random keys which
154  * ALL can fit in hash table (no errors)
155  */
156 static int
157 get_input_keys(unsigned int with_pushes, unsigned int table_index,
158 							unsigned int ext)
159 {
160 	unsigned i, j;
161 	unsigned bucket_idx, incr, success = 1;
162 	uint8_t k = 0;
163 	int32_t ret;
164 	const uint32_t bucket_bitmask = NUM_BUCKETS - 1;
165 	unsigned int keys_to_add;
166 
167 	if (!ext)
168 		keys_to_add = KEYS_TO_ADD * ADD_PERCENT;
169 	else
170 		keys_to_add = KEYS_TO_ADD;
171 	/* Reset all arrays */
172 	for (i = 0; i < MAX_ENTRIES; i++)
173 		slot_taken[i] = 0;
174 
175 	for (i = 0; i < NUM_BUCKETS; i++)
176 		buckets[i] = 0;
177 
178 	for (j = 0; j < hashtest_key_lens[table_index]; j++)
179 		keys[0][j] = 0;
180 
181 	/*
182 	 * Add only entries that are not duplicated and that fits in the table
183 	 * (cannot store more than BUCKET_SIZE entries in a bucket).
184 	 * Regardless a key has been added correctly or not (success),
185 	 * the next one to try will be increased by 1.
186 	 */
187 	for (i = 0; i < keys_to_add;) {
188 		incr = 0;
189 		if (i != 0) {
190 			keys[i][0] = ++k;
191 			/* Overflow, need to increment the next byte */
192 			if (keys[i][0] == 0)
193 				incr = 1;
194 			for (j = 1; j < hashtest_key_lens[table_index]; j++) {
195 				/* Do not increase next byte */
196 				if (incr == 0)
197 					if (success == 1)
198 						keys[i][j] = keys[i - 1][j];
199 					else
200 						keys[i][j] = keys[i][j];
201 				/* Increase next byte by one */
202 				else {
203 					if (success == 1)
204 						keys[i][j] = keys[i-1][j] + 1;
205 					else
206 						keys[i][j] = keys[i][j] + 1;
207 					if (keys[i][j] == 0)
208 						incr = 1;
209 					else
210 						incr = 0;
211 				}
212 			}
213 		}
214 		success = 0;
215 		signatures[i] = rte_hash_hash(h[table_index], keys[i]);
216 		bucket_idx = signatures[i] & bucket_bitmask;
217 		/*
218 		 * If we are not inserting keys in secondary location,
219 		 * when bucket is full, do not try to insert the key
220 		 */
221 		if (with_pushes == 0)
222 			if (buckets[bucket_idx] == BUCKET_SIZE)
223 				continue;
224 
225 		/* If key can be added, leave in successful key arrays "keys" */
226 		ret = rte_hash_add_key_with_hash(h[table_index], keys[i],
227 						signatures[i]);
228 		if (ret >= 0) {
229 			/* If key is already added, ignore the entry and do not store */
230 			if (slot_taken[ret])
231 				continue;
232 			else {
233 				/* Store the returned position and mark slot as taken */
234 				slot_taken[ret] = 1;
235 				positions[i] = ret;
236 				buckets[bucket_idx]++;
237 				success = 1;
238 				i++;
239 			}
240 		}
241 	}
242 
243 	/* Reset the table, so we can measure the time to add all the entries */
244 	rte_hash_free(h[table_index]);
245 	h[table_index] = rte_hash_create(&ut_params);
246 
247 	return 0;
248 }
249 
250 static int
251 timed_adds(unsigned int with_hash, unsigned int with_data,
252 				unsigned int table_index, unsigned int ext)
253 {
254 	unsigned i;
255 	const uint64_t start_tsc = rte_rdtsc();
256 	void *data;
257 	int32_t ret;
258 	unsigned int keys_to_add;
259 	if (!ext)
260 		keys_to_add = KEYS_TO_ADD * ADD_PERCENT;
261 	else
262 		keys_to_add = KEYS_TO_ADD;
263 
264 	for (i = 0; i < keys_to_add; i++) {
265 		data = (void *) ((uintptr_t) signatures[i]);
266 		if (with_hash && with_data) {
267 			ret = rte_hash_add_key_with_hash_data(h[table_index],
268 						(const void *) keys[i],
269 						signatures[i], data);
270 			if (ret < 0) {
271 				printf("H+D: Failed to add key number %u\n", i);
272 				return -1;
273 			}
274 		} else if (with_hash && !with_data) {
275 			ret = rte_hash_add_key_with_hash(h[table_index],
276 						(const void *) keys[i],
277 						signatures[i]);
278 			if (ret >= 0)
279 				positions[i] = ret;
280 			else {
281 				printf("H: Failed to add key number %u\n", i);
282 				return -1;
283 			}
284 		} else if (!with_hash && with_data) {
285 			ret = rte_hash_add_key_data(h[table_index],
286 						(const void *) keys[i],
287 						data);
288 			if (ret < 0) {
289 				printf("D: Failed to add key number %u\n", i);
290 				return -1;
291 			}
292 		} else {
293 			ret = rte_hash_add_key(h[table_index], keys[i]);
294 			if (ret >= 0)
295 				positions[i] = ret;
296 			else {
297 				printf("Failed to add key number %u\n", i);
298 				return -1;
299 			}
300 		}
301 	}
302 
303 	const uint64_t end_tsc = rte_rdtsc();
304 	const uint64_t time_taken = end_tsc - start_tsc;
305 
306 	cycles[table_index][OP_ADD][with_hash][with_data] = time_taken/keys_to_add;
307 
308 	return 0;
309 }
310 
311 static int
312 timed_lookups(unsigned int with_hash, unsigned int with_data,
313 				unsigned int table_index, unsigned int ext)
314 {
315 	unsigned i, j;
316 	const uint64_t start_tsc = rte_rdtsc();
317 	void *ret_data;
318 	void *expected_data;
319 	int32_t ret;
320 	unsigned int keys_to_add, num_lookups;
321 
322 	if (!ext) {
323 		keys_to_add = KEYS_TO_ADD * ADD_PERCENT;
324 		num_lookups = NUM_LOOKUPS * ADD_PERCENT;
325 	} else {
326 		keys_to_add = KEYS_TO_ADD;
327 		num_lookups = NUM_LOOKUPS;
328 	}
329 	for (i = 0; i < num_lookups / keys_to_add; i++) {
330 		for (j = 0; j < keys_to_add; j++) {
331 			if (with_hash && with_data) {
332 				ret = rte_hash_lookup_with_hash_data(h[table_index],
333 							(const void *) keys[j],
334 							signatures[j], &ret_data);
335 				if (ret < 0) {
336 					printf("Key number %u was not found\n", j);
337 					return -1;
338 				}
339 				expected_data = (void *) ((uintptr_t) signatures[j]);
340 				if (ret_data != expected_data) {
341 					printf("Data returned for key number %u is %p,"
342 					       " but should be %p\n", j, ret_data,
343 						expected_data);
344 					return -1;
345 				}
346 			} else if (with_hash && !with_data) {
347 				ret = rte_hash_lookup_with_hash(h[table_index],
348 							(const void *) keys[j],
349 							signatures[j]);
350 				if (ret < 0 || ret != positions[j]) {
351 					printf("Key looked up in %d, should be in %d\n",
352 						ret, positions[j]);
353 					return -1;
354 				}
355 			} else if (!with_hash && with_data) {
356 				ret = rte_hash_lookup_data(h[table_index],
357 							(const void *) keys[j], &ret_data);
358 				if (ret < 0) {
359 					printf("Key number %u was not found\n", j);
360 					return -1;
361 				}
362 				expected_data = (void *) ((uintptr_t) signatures[j]);
363 				if (ret_data != expected_data) {
364 					printf("Data returned for key number %u is %p,"
365 					       " but should be %p\n", j, ret_data,
366 						expected_data);
367 					return -1;
368 				}
369 			} else {
370 				ret = rte_hash_lookup(h[table_index], keys[j]);
371 				if (ret < 0 || ret != positions[j]) {
372 					printf("Key looked up in %d, should be in %d\n",
373 						ret, positions[j]);
374 					return -1;
375 				}
376 			}
377 		}
378 	}
379 
380 	const uint64_t end_tsc = rte_rdtsc();
381 	const uint64_t time_taken = end_tsc - start_tsc;
382 
383 	cycles[table_index][OP_LOOKUP][with_hash][with_data] = time_taken/num_lookups;
384 
385 	return 0;
386 }
387 
388 static int
389 timed_lookups_multi(unsigned int with_hash, unsigned int with_data,
390 		unsigned int table_index, unsigned int ext)
391 {
392 	unsigned i, j, k;
393 	int32_t positions_burst[BURST_SIZE];
394 	const void *keys_burst[BURST_SIZE];
395 	void *expected_data[BURST_SIZE];
396 	void *ret_data[BURST_SIZE];
397 	uint64_t hit_mask;
398 	int ret;
399 	unsigned int keys_to_add, num_lookups;
400 
401 	if (!ext) {
402 		keys_to_add = KEYS_TO_ADD * ADD_PERCENT;
403 		num_lookups = NUM_LOOKUPS * ADD_PERCENT;
404 	} else {
405 		keys_to_add = KEYS_TO_ADD;
406 		num_lookups = NUM_LOOKUPS;
407 	}
408 
409 	const uint64_t start_tsc = rte_rdtsc();
410 
411 	for (i = 0; i < num_lookups/keys_to_add; i++) {
412 		for (j = 0; j < keys_to_add/BURST_SIZE; j++) {
413 			for (k = 0; k < BURST_SIZE; k++)
414 				keys_burst[k] = keys[j * BURST_SIZE + k];
415 			if (!with_hash && with_data) {
416 				ret = rte_hash_lookup_bulk_data(h[table_index],
417 					(const void **) keys_burst,
418 					BURST_SIZE,
419 					&hit_mask,
420 					ret_data);
421 				if (ret != BURST_SIZE) {
422 					printf("Expect to find %u keys,"
423 					       " but found %d\n", BURST_SIZE, ret);
424 					return -1;
425 				}
426 				for (k = 0; k < BURST_SIZE; k++) {
427 					if ((hit_mask & (1ULL << k))  == 0) {
428 						printf("Key number %u not found\n",
429 							j * BURST_SIZE + k);
430 						return -1;
431 					}
432 					expected_data[k] = (void *) ((uintptr_t) signatures[j * BURST_SIZE + k]);
433 					if (ret_data[k] != expected_data[k]) {
434 						printf("Data returned for key number %u is %p,"
435 						       " but should be %p\n", j * BURST_SIZE + k,
436 							ret_data[k], expected_data[k]);
437 						return -1;
438 					}
439 				}
440 			} else if (with_hash && with_data) {
441 				ret = rte_hash_lookup_with_hash_bulk_data(
442 					h[table_index],
443 					(const void **)keys_burst,
444 					&signatures[j * BURST_SIZE],
445 					BURST_SIZE, &hit_mask, ret_data);
446 				if (ret != BURST_SIZE) {
447 					printf("Expect to find %u keys,"
448 					       " but found %d\n",
449 						BURST_SIZE, ret);
450 					return -1;
451 				}
452 				for (k = 0; k < BURST_SIZE; k++) {
453 					if ((hit_mask & (1ULL << k))  == 0) {
454 						printf("Key number %u"
455 							" not found\n",
456 							j * BURST_SIZE + k);
457 						return -1;
458 					}
459 					expected_data[k] =
460 						(void *)((uintptr_t)signatures[
461 						j * BURST_SIZE + k]);
462 					if (ret_data[k] != expected_data[k]) {
463 						printf("Data returned for key"
464 							" number %u is %p,"
465 							" but should be %p\n",
466 							j * BURST_SIZE + k,
467 							ret_data[k],
468 							expected_data[k]);
469 						return -1;
470 					}
471 				}
472 			} else if (with_hash && !with_data) {
473 				ret = rte_hash_lookup_with_hash_bulk(
474 					h[table_index],
475 					(const void **)keys_burst,
476 					&signatures[j * BURST_SIZE],
477 					BURST_SIZE, positions_burst);
478 				for (k = 0; k < BURST_SIZE; k++) {
479 					if (positions_burst[k] !=
480 							positions[j *
481 							BURST_SIZE + k]) {
482 						printf("Key looked up in %d, should be in %d\n",
483 							positions_burst[k],
484 							positions[j *
485 							BURST_SIZE + k]);
486 						return -1;
487 					}
488 				}
489 			} else {
490 				rte_hash_lookup_bulk(h[table_index],
491 						(const void **) keys_burst,
492 						BURST_SIZE,
493 						positions_burst);
494 				for (k = 0; k < BURST_SIZE; k++) {
495 					if (positions_burst[k] != positions[j * BURST_SIZE + k]) {
496 						printf("Key looked up in %d, should be in %d\n",
497 							positions_burst[k],
498 							positions[j * BURST_SIZE + k]);
499 						return -1;
500 					}
501 				}
502 			}
503 		}
504 	}
505 
506 	const uint64_t end_tsc = rte_rdtsc();
507 	const uint64_t time_taken = end_tsc - start_tsc;
508 
509 	cycles[table_index][OP_LOOKUP_MULTI][with_hash][with_data] =
510 		time_taken/num_lookups;
511 
512 	return 0;
513 }
514 
515 static int
516 timed_deletes(unsigned int with_hash, unsigned int with_data,
517 				unsigned int table_index, unsigned int ext)
518 {
519 	unsigned i;
520 	const uint64_t start_tsc = rte_rdtsc();
521 	int32_t ret;
522 	unsigned int keys_to_add;
523 	if (!ext)
524 		keys_to_add = KEYS_TO_ADD * ADD_PERCENT;
525 	else
526 		keys_to_add = KEYS_TO_ADD;
527 
528 	for (i = 0; i < keys_to_add; i++) {
529 		/* There are no delete functions with data, so just call two functions */
530 		if (with_hash)
531 			ret = rte_hash_del_key_with_hash(h[table_index],
532 							(const void *) keys[i],
533 							signatures[i]);
534 		else
535 			ret = rte_hash_del_key(h[table_index],
536 							(const void *) keys[i]);
537 		if (ret >= 0)
538 			positions[i] = ret;
539 		else {
540 			printf("Failed to delete key number %u\n", i);
541 			return -1;
542 		}
543 	}
544 
545 	const uint64_t end_tsc = rte_rdtsc();
546 	const uint64_t time_taken = end_tsc - start_tsc;
547 
548 	cycles[table_index][OP_DELETE][with_hash][with_data] = time_taken/keys_to_add;
549 
550 	return 0;
551 }
552 
553 static void
554 free_table(unsigned table_index)
555 {
556 	rte_hash_free(h[table_index]);
557 }
558 
559 static void
560 reset_table(unsigned table_index)
561 {
562 	rte_hash_reset(h[table_index]);
563 }
564 
565 static int
566 run_all_tbl_perf_tests(unsigned int with_pushes, unsigned int with_locks,
567 						unsigned int ext)
568 {
569 	unsigned i, j, with_data, with_hash;
570 
571 	printf("Measuring performance, please wait");
572 	fflush(stdout);
573 
574 	for (with_data = 0; with_data <= 1; with_data++) {
575 		for (i = 0; i < NUM_KEYSIZES; i++) {
576 			if (create_table(with_data, i, with_locks, ext) < 0)
577 				return -1;
578 
579 			if (get_input_keys(with_pushes, i, ext) < 0)
580 				return -1;
581 			for (with_hash = 0; with_hash <= 1; with_hash++) {
582 				if (timed_adds(with_hash, with_data, i, ext) < 0)
583 					return -1;
584 
585 				for (j = 0; j < NUM_SHUFFLES; j++)
586 					shuffle_input_keys(i, ext);
587 
588 				if (timed_lookups(with_hash, with_data, i, ext) < 0)
589 					return -1;
590 
591 				if (timed_lookups_multi(with_hash, with_data,
592 						i, ext) < 0)
593 					return -1;
594 
595 				if (timed_deletes(with_hash, with_data, i, ext) < 0)
596 					return -1;
597 
598 				/* Print a dot to show progress on operations */
599 				printf(".");
600 				fflush(stdout);
601 
602 				reset_table(i);
603 			}
604 			free_table(i);
605 		}
606 	}
607 
608 	printf("\nResults (in CPU cycles/operation)\n");
609 	printf("-----------------------------------\n");
610 	for (with_data = 0; with_data <= 1; with_data++) {
611 		if (with_data)
612 			printf("\n Operations with 8-byte data\n");
613 		else
614 			printf("\n Operations without data\n");
615 		for (with_hash = 0; with_hash <= 1; with_hash++) {
616 			if (with_hash)
617 				printf("\nWith pre-computed hash values\n");
618 			else
619 				printf("\nWithout pre-computed hash values\n");
620 
621 			printf("\n%-18s%-18s%-18s%-18s%-18s\n",
622 			"Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
623 			for (i = 0; i < NUM_KEYSIZES; i++) {
624 				printf("%-18d", hashtest_key_lens[i]);
625 				for (j = 0; j < NUM_OPERATIONS; j++)
626 					printf("%-18"PRIu64, cycles[i][j][with_hash][with_data]);
627 				printf("\n");
628 			}
629 		}
630 	}
631 	return 0;
632 }
633 
634 /* Control operation of performance testing of fbk hash. */
635 #define LOAD_FACTOR 0.667	/* How full to make the hash table. */
636 #define TEST_SIZE 1000000	/* How many operations to time. */
637 #define TEST_ITERATIONS 30	/* How many measurements to take. */
638 #define ENTRIES (1 << 15)	/* How many entries. */
639 
640 static int
641 fbk_hash_perf_test(void)
642 {
643 	struct rte_fbk_hash_params params = {
644 		.name = "fbk_hash_test",
645 		.entries = ENTRIES,
646 		.entries_per_bucket = 4,
647 		.socket_id = rte_socket_id(),
648 	};
649 	struct rte_fbk_hash_table *handle = NULL;
650 	uint32_t *keys = NULL;
651 	unsigned indexes[TEST_SIZE];
652 	uint64_t lookup_time = 0;
653 	unsigned added = 0;
654 	unsigned value = 0;
655 	uint32_t key;
656 	uint16_t val;
657 	unsigned i, j;
658 
659 	handle = rte_fbk_hash_create(&params);
660 	if (handle == NULL) {
661 		printf("Error creating table\n");
662 		return -1;
663 	}
664 
665 	keys = rte_zmalloc(NULL, ENTRIES * sizeof(*keys), 0);
666 	if (keys == NULL) {
667 		printf("fbk hash: memory allocation for key store failed\n");
668 		return -1;
669 	}
670 
671 	/* Generate random keys and values. */
672 	for (i = 0; i < ENTRIES; i++) {
673 		key = (uint32_t)rte_rand();
674 		key = ((uint64_t)key << 32) | (uint64_t)rte_rand();
675 		val = (uint16_t)rte_rand();
676 
677 		if (rte_fbk_hash_add_key(handle, key, val) == 0) {
678 			keys[added] = key;
679 			added++;
680 		}
681 		if (added > (LOAD_FACTOR * ENTRIES))
682 			break;
683 	}
684 
685 	for (i = 0; i < TEST_ITERATIONS; i++) {
686 		uint64_t begin;
687 		uint64_t end;
688 
689 		/* Generate random indexes into keys[] array. */
690 		for (j = 0; j < TEST_SIZE; j++)
691 			indexes[j] = rte_rand() % added;
692 
693 		begin = rte_rdtsc();
694 		/* Do lookups */
695 		for (j = 0; j < TEST_SIZE; j++)
696 			value += rte_fbk_hash_lookup(handle, keys[indexes[j]]);
697 
698 		end = rte_rdtsc();
699 		lookup_time += (double)(end - begin);
700 	}
701 
702 	printf("\n\n *** FBK Hash function performance test results ***\n");
703 	/*
704 	 * The use of the 'value' variable ensures that the hash lookup is not
705 	 * being optimised out by the compiler.
706 	 */
707 	if (value != 0)
708 		printf("Number of ticks per lookup = %g\n",
709 			(double)lookup_time /
710 			((double)TEST_ITERATIONS * (double)TEST_SIZE));
711 
712 	rte_fbk_hash_free(handle);
713 
714 	return 0;
715 }
716 
717 static int
718 test_hash_perf(void)
719 {
720 	unsigned int with_pushes, with_locks;
721 
722 	if (RTE_EXEC_ENV_IS_WINDOWS)
723 		return TEST_SKIPPED;
724 
725 	for (with_locks = 0; with_locks <= 1; with_locks++) {
726 		if (with_locks)
727 			printf("\nWith locks in the code\n");
728 		else
729 			printf("\nWithout locks in the code\n");
730 		for (with_pushes = 0; with_pushes <= 1; with_pushes++) {
731 			if (with_pushes == 0)
732 				printf("\nALL ELEMENTS IN PRIMARY LOCATION\n");
733 			else
734 				printf("\nELEMENTS IN PRIMARY OR SECONDARY LOCATION\n");
735 			if (run_all_tbl_perf_tests(with_pushes, with_locks, 0) < 0)
736 				return -1;
737 		}
738 	}
739 
740 	printf("\n EXTENDABLE BUCKETS PERFORMANCE\n");
741 
742 	if (run_all_tbl_perf_tests(1, 0, 1) < 0)
743 		return -1;
744 
745 	if (fbk_hash_perf_test() < 0)
746 		return -1;
747 
748 	return 0;
749 }
750 
751 REGISTER_TEST_COMMAND(hash_perf_autotest, test_hash_perf);
752