xref: /dpdk/app/test-compress-perf/main.c (revision 5ecb687a5698d2d8ec1f3b3b5a7a16bceca3e29c)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4 
5 #include <rte_malloc.h>
6 #include <rte_eal.h>
7 #include <rte_log.h>
8 #include <rte_compressdev.h>
9 
10 #include "comp_perf_options.h"
11 #include "comp_perf_test_verify.h"
12 #include "comp_perf_test_benchmark.h"
13 
14 #define NUM_MAX_XFORMS 16
15 #define NUM_MAX_INFLIGHT_OPS 512
16 
17 #define DIV_CEIL(a, b)  ((a) / (b) + ((a) % (b) != 0))
18 
19 /* Cleanup state machine */
20 static enum cleanup_st {
21 	ST_CLEAR = 0,
22 	ST_TEST_DATA,
23 	ST_COMPDEV,
24 	ST_INPUT_DATA,
25 	ST_MEMORY_ALLOC,
26 	ST_PREPARE_BUF,
27 	ST_DURING_TEST
28 } cleanup = ST_CLEAR;
29 
30 static int
31 param_range_check(uint16_t size, const struct rte_param_log2_range *range)
32 {
33 	unsigned int next_size;
34 
35 	/* Check lower/upper bounds */
36 	if (size < range->min)
37 		return -1;
38 
39 	if (size > range->max)
40 		return -1;
41 
42 	/* If range is actually only one value, size is correct */
43 	if (range->increment == 0)
44 		return 0;
45 
46 	/* Check if value is one of the supported sizes */
47 	for (next_size = range->min; next_size <= range->max;
48 			next_size += range->increment)
49 		if (size == next_size)
50 			return 0;
51 
52 	return -1;
53 }
54 
55 static int
56 comp_perf_check_capabilities(struct comp_test_data *test_data)
57 {
58 	const struct rte_compressdev_capabilities *cap;
59 
60 	cap = rte_compressdev_capability_get(test_data->cdev_id,
61 					     RTE_COMP_ALGO_DEFLATE);
62 
63 	if (cap == NULL) {
64 		RTE_LOG(ERR, USER1,
65 			"Compress device does not support DEFLATE\n");
66 		return -1;
67 	}
68 
69 	uint64_t comp_flags = cap->comp_feature_flags;
70 
71 	/* Huffman enconding */
72 	if (test_data->huffman_enc == RTE_COMP_HUFFMAN_FIXED &&
73 			(comp_flags & RTE_COMP_FF_HUFFMAN_FIXED) == 0) {
74 		RTE_LOG(ERR, USER1,
75 			"Compress device does not supported Fixed Huffman\n");
76 		return -1;
77 	}
78 
79 	if (test_data->huffman_enc == RTE_COMP_HUFFMAN_DYNAMIC &&
80 			(comp_flags & RTE_COMP_FF_HUFFMAN_DYNAMIC) == 0) {
81 		RTE_LOG(ERR, USER1,
82 			"Compress device does not supported Dynamic Huffman\n");
83 		return -1;
84 	}
85 
86 	/* Window size */
87 	if (test_data->window_sz != -1) {
88 		if (param_range_check(test_data->window_sz, &cap->window_size)
89 				< 0) {
90 			RTE_LOG(ERR, USER1,
91 				"Compress device does not support "
92 				"this window size\n");
93 			return -1;
94 		}
95 	} else
96 		/* Set window size to PMD maximum if none was specified */
97 		test_data->window_sz = cap->window_size.max;
98 
99 	/* Check if chained mbufs is supported */
100 	if (test_data->max_sgl_segs > 1  &&
101 			(comp_flags & RTE_COMP_FF_OOP_SGL_IN_SGL_OUT) == 0) {
102 		RTE_LOG(INFO, USER1, "Compress device does not support "
103 				"chained mbufs. Max SGL segments set to 1\n");
104 		test_data->max_sgl_segs = 1;
105 	}
106 
107 	/* Level 0 support */
108 	if (test_data->level.min == 0 &&
109 			(comp_flags & RTE_COMP_FF_NONCOMPRESSED_BLOCKS) == 0) {
110 		RTE_LOG(ERR, USER1, "Compress device does not support "
111 				"level 0 (no compression)\n");
112 		return -1;
113 	}
114 
115 	return 0;
116 }
117 
118 static uint32_t
119 find_buf_size(uint32_t input_size)
120 {
121 	uint32_t i;
122 
123 	/* From performance point of view the buffer size should be a
124 	 * power of 2 but also should be enough to store incompressible data
125 	 */
126 
127 	/* We're looking for nearest power of 2 buffer size, which is greather
128 	 * than input_size
129 	 */
130 	uint32_t size =
131 		!input_size ? MIN_COMPRESSED_BUF_SIZE : (input_size << 1);
132 
133 	for (i = UINT16_MAX + 1; !(i & size); i >>= 1)
134 		;
135 
136 	return i > ((UINT16_MAX + 1) >> 1)
137 			? (uint32_t)((float)input_size * EXPANSE_RATIO)
138 			: i;
139 }
140 
141 static int
142 comp_perf_allocate_memory(struct comp_test_data *test_data)
143 {
144 
145 	test_data->out_seg_sz = find_buf_size(test_data->seg_sz);
146 	/* Number of segments for input and output
147 	 * (compression and decompression)
148 	 */
149 	uint32_t total_segs = DIV_CEIL(test_data->input_data_sz,
150 			test_data->seg_sz);
151 	test_data->comp_buf_pool = rte_pktmbuf_pool_create("comp_buf_pool",
152 				total_segs,
153 				0, 0,
154 				test_data->out_seg_sz + RTE_PKTMBUF_HEADROOM,
155 				rte_socket_id());
156 	if (test_data->comp_buf_pool == NULL) {
157 		RTE_LOG(ERR, USER1, "Mbuf mempool could not be created\n");
158 		return -1;
159 	}
160 
161 	cleanup = ST_MEMORY_ALLOC;
162 	test_data->decomp_buf_pool = rte_pktmbuf_pool_create("decomp_buf_pool",
163 				total_segs,
164 				0, 0, test_data->seg_sz + RTE_PKTMBUF_HEADROOM,
165 				rte_socket_id());
166 	if (test_data->decomp_buf_pool == NULL) {
167 		RTE_LOG(ERR, USER1, "Mbuf mempool could not be created\n");
168 		return -1;
169 	}
170 
171 	test_data->total_bufs = DIV_CEIL(total_segs, test_data->max_sgl_segs);
172 
173 	test_data->op_pool = rte_comp_op_pool_create("op_pool",
174 				  test_data->total_bufs,
175 				  0, 0, rte_socket_id());
176 	if (test_data->op_pool == NULL) {
177 		RTE_LOG(ERR, USER1, "Comp op mempool could not be created\n");
178 		return -1;
179 	}
180 
181 	/*
182 	 * Compressed data might be a bit larger than input data,
183 	 * if data cannot be compressed
184 	 */
185 	test_data->compressed_data = rte_zmalloc_socket(NULL,
186 				test_data->input_data_sz * EXPANSE_RATIO
187 						+ MIN_COMPRESSED_BUF_SIZE, 0,
188 				rte_socket_id());
189 	if (test_data->compressed_data == NULL) {
190 		RTE_LOG(ERR, USER1, "Memory to hold the data from the input "
191 				"file could not be allocated\n");
192 		return -1;
193 	}
194 
195 	test_data->decompressed_data = rte_zmalloc_socket(NULL,
196 				test_data->input_data_sz, 0,
197 				rte_socket_id());
198 	if (test_data->decompressed_data == NULL) {
199 		RTE_LOG(ERR, USER1, "Memory to hold the data from the input "
200 				"file could not be allocated\n");
201 		return -1;
202 	}
203 
204 	test_data->comp_bufs = rte_zmalloc_socket(NULL,
205 			test_data->total_bufs * sizeof(struct rte_mbuf *),
206 			0, rte_socket_id());
207 	if (test_data->comp_bufs == NULL) {
208 		RTE_LOG(ERR, USER1, "Memory to hold the compression mbufs"
209 				" could not be allocated\n");
210 		return -1;
211 	}
212 
213 	test_data->decomp_bufs = rte_zmalloc_socket(NULL,
214 			test_data->total_bufs * sizeof(struct rte_mbuf *),
215 			0, rte_socket_id());
216 	if (test_data->decomp_bufs == NULL) {
217 		RTE_LOG(ERR, USER1, "Memory to hold the decompression mbufs"
218 				" could not be allocated\n");
219 		return -1;
220 	}
221 	return 0;
222 }
223 
224 static int
225 comp_perf_dump_input_data(struct comp_test_data *test_data)
226 {
227 	FILE *f = fopen(test_data->input_file, "r");
228 	int ret = -1;
229 
230 	if (f == NULL) {
231 		RTE_LOG(ERR, USER1, "Input file could not be opened\n");
232 		return -1;
233 	}
234 
235 	if (fseek(f, 0, SEEK_END) != 0) {
236 		RTE_LOG(ERR, USER1, "Size of input could not be calculated\n");
237 		goto end;
238 	}
239 	size_t actual_file_sz = ftell(f);
240 	/* If extended input data size has not been set,
241 	 * input data size = file size
242 	 */
243 
244 	if (test_data->input_data_sz == 0)
245 		test_data->input_data_sz = actual_file_sz;
246 
247 	if (fseek(f, 0, SEEK_SET) != 0) {
248 		RTE_LOG(ERR, USER1, "Size of input could not be calculated\n");
249 		goto end;
250 	}
251 
252 	test_data->input_data = rte_zmalloc_socket(NULL,
253 				test_data->input_data_sz, 0, rte_socket_id());
254 
255 	if (test_data->input_data == NULL) {
256 		RTE_LOG(ERR, USER1, "Memory to hold the data from the input "
257 				"file could not be allocated\n");
258 		goto end;
259 	}
260 
261 	size_t remaining_data = test_data->input_data_sz;
262 	uint8_t *data = test_data->input_data;
263 
264 	while (remaining_data > 0) {
265 		size_t data_to_read = RTE_MIN(remaining_data, actual_file_sz);
266 
267 		if (fread(data, data_to_read, 1, f) != 1) {
268 			RTE_LOG(ERR, USER1, "Input file could not be read\n");
269 			goto end;
270 		}
271 		if (fseek(f, 0, SEEK_SET) != 0) {
272 			RTE_LOG(ERR, USER1,
273 				"Size of input could not be calculated\n");
274 			goto end;
275 		}
276 		remaining_data -= data_to_read;
277 		data += data_to_read;
278 	}
279 
280 	if (test_data->input_data_sz > actual_file_sz)
281 		RTE_LOG(INFO, USER1,
282 		  "%zu bytes read from file %s, extending the file %.2f times\n",
283 			test_data->input_data_sz, test_data->input_file,
284 			(double)test_data->input_data_sz/actual_file_sz);
285 	else
286 		RTE_LOG(INFO, USER1,
287 			"%zu bytes read from file %s\n",
288 			test_data->input_data_sz, test_data->input_file);
289 
290 	ret = 0;
291 
292 end:
293 	fclose(f);
294 	return ret;
295 }
296 
297 static int
298 comp_perf_initialize_compressdev(struct comp_test_data *test_data)
299 {
300 	uint8_t enabled_cdev_count;
301 	uint8_t enabled_cdevs[RTE_COMPRESS_MAX_DEVS];
302 
303 	enabled_cdev_count = rte_compressdev_devices_get(test_data->driver_name,
304 			enabled_cdevs, RTE_COMPRESS_MAX_DEVS);
305 	if (enabled_cdev_count == 0) {
306 		RTE_LOG(ERR, USER1, "No compress devices type %s available\n",
307 				test_data->driver_name);
308 		return -EINVAL;
309 	}
310 
311 	if (enabled_cdev_count > 1)
312 		RTE_LOG(INFO, USER1,
313 			"Only the first compress device will be used\n");
314 
315 	test_data->cdev_id = enabled_cdevs[0];
316 
317 	if (comp_perf_check_capabilities(test_data) < 0)
318 		return -1;
319 
320 	/* Configure compressdev (one device, one queue pair) */
321 	struct rte_compressdev_config config = {
322 		.socket_id = rte_socket_id(),
323 		.nb_queue_pairs = 1,
324 		.max_nb_priv_xforms = NUM_MAX_XFORMS,
325 		.max_nb_streams = 0
326 	};
327 
328 	if (rte_compressdev_configure(test_data->cdev_id, &config) < 0) {
329 		RTE_LOG(ERR, USER1, "Device configuration failed\n");
330 		return -1;
331 	}
332 
333 	if (rte_compressdev_queue_pair_setup(test_data->cdev_id, 0,
334 			NUM_MAX_INFLIGHT_OPS, rte_socket_id()) < 0) {
335 		RTE_LOG(ERR, USER1, "Queue pair setup failed\n");
336 		return -1;
337 	}
338 
339 	if (rte_compressdev_start(test_data->cdev_id) < 0) {
340 		RTE_LOG(ERR, USER1, "Device could not be started\n");
341 		return -1;
342 	}
343 
344 	return 0;
345 }
346 
347 static int
348 prepare_bufs(struct comp_test_data *test_data)
349 {
350 	uint32_t remaining_data = test_data->input_data_sz;
351 	uint8_t *input_data_ptr = test_data->input_data;
352 	size_t data_sz;
353 	uint8_t *data_addr;
354 	uint32_t i, j;
355 
356 	for (i = 0; i < test_data->total_bufs; i++) {
357 		/* Allocate data in input mbuf and copy data from input file */
358 		test_data->decomp_bufs[i] =
359 			rte_pktmbuf_alloc(test_data->decomp_buf_pool);
360 		if (test_data->decomp_bufs[i] == NULL) {
361 			RTE_LOG(ERR, USER1, "Could not allocate mbuf\n");
362 			return -1;
363 		}
364 
365 		cleanup = ST_PREPARE_BUF;
366 		data_sz = RTE_MIN(remaining_data, test_data->seg_sz);
367 		data_addr = (uint8_t *) rte_pktmbuf_append(
368 					test_data->decomp_bufs[i], data_sz);
369 		if (data_addr == NULL) {
370 			RTE_LOG(ERR, USER1, "Could not append data\n");
371 			return -1;
372 		}
373 		rte_memcpy(data_addr, input_data_ptr, data_sz);
374 
375 		input_data_ptr += data_sz;
376 		remaining_data -= data_sz;
377 
378 		/* Already one segment in the mbuf */
379 		uint16_t segs_per_mbuf = 1;
380 
381 		/* Chain mbufs if needed for input mbufs */
382 		while (segs_per_mbuf < test_data->max_sgl_segs
383 				&& remaining_data > 0) {
384 			struct rte_mbuf *next_seg =
385 				rte_pktmbuf_alloc(test_data->decomp_buf_pool);
386 
387 			if (next_seg == NULL) {
388 				RTE_LOG(ERR, USER1,
389 					"Could not allocate mbuf\n");
390 				return -1;
391 			}
392 
393 			data_sz = RTE_MIN(remaining_data, test_data->seg_sz);
394 			data_addr = (uint8_t *)rte_pktmbuf_append(next_seg,
395 				data_sz);
396 
397 			if (data_addr == NULL) {
398 				RTE_LOG(ERR, USER1, "Could not append data\n");
399 				return -1;
400 			}
401 
402 			rte_memcpy(data_addr, input_data_ptr, data_sz);
403 			input_data_ptr += data_sz;
404 			remaining_data -= data_sz;
405 
406 			if (rte_pktmbuf_chain(test_data->decomp_bufs[i],
407 					next_seg) < 0) {
408 				RTE_LOG(ERR, USER1, "Could not chain mbufs\n");
409 				return -1;
410 			}
411 			segs_per_mbuf++;
412 		}
413 
414 		/* Allocate data in output mbuf */
415 		test_data->comp_bufs[i] =
416 			rte_pktmbuf_alloc(test_data->comp_buf_pool);
417 		if (test_data->comp_bufs[i] == NULL) {
418 			RTE_LOG(ERR, USER1, "Could not allocate mbuf\n");
419 			return -1;
420 		}
421 		data_addr = (uint8_t *) rte_pktmbuf_append(
422 					test_data->comp_bufs[i],
423 					test_data->out_seg_sz);
424 		if (data_addr == NULL) {
425 			RTE_LOG(ERR, USER1, "Could not append data\n");
426 			return -1;
427 		}
428 
429 		/* Chain mbufs if needed for output mbufs */
430 		for (j = 1; j < segs_per_mbuf; j++) {
431 			struct rte_mbuf *next_seg =
432 				rte_pktmbuf_alloc(test_data->comp_buf_pool);
433 
434 			if (next_seg == NULL) {
435 				RTE_LOG(ERR, USER1,
436 					"Could not allocate mbuf\n");
437 				return -1;
438 			}
439 
440 			data_addr = (uint8_t *)rte_pktmbuf_append(next_seg,
441 				test_data->out_seg_sz);
442 
443 			if (data_addr == NULL) {
444 				RTE_LOG(ERR, USER1, "Could not append data\n");
445 				return -1;
446 			}
447 
448 			if (rte_pktmbuf_chain(test_data->comp_bufs[i],
449 					next_seg) < 0) {
450 				RTE_LOG(ERR, USER1, "Could not chain mbufs\n");
451 				return -1;
452 			}
453 		}
454 	}
455 
456 	return 0;
457 }
458 
459 static void
460 free_bufs(struct comp_test_data *test_data)
461 {
462 	uint32_t i;
463 
464 	for (i = 0; i < test_data->total_bufs; i++) {
465 		rte_pktmbuf_free(test_data->comp_bufs[i]);
466 		rte_pktmbuf_free(test_data->decomp_bufs[i]);
467 	}
468 }
469 
470 
471 
472 int
473 main(int argc, char **argv)
474 {
475 	uint8_t level, level_idx = 0;
476 	int ret, i;
477 	struct comp_test_data *test_data;
478 
479 	/* Initialise DPDK EAL */
480 	ret = rte_eal_init(argc, argv);
481 	if (ret < 0)
482 		rte_exit(EXIT_FAILURE, "Invalid EAL arguments!\n");
483 	argc -= ret;
484 	argv += ret;
485 
486 	test_data = rte_zmalloc_socket(NULL, sizeof(struct comp_test_data),
487 					0, rte_socket_id());
488 
489 	if (test_data == NULL)
490 		rte_exit(EXIT_FAILURE, "Cannot reserve memory in socket %d\n",
491 				rte_socket_id());
492 
493 	ret = EXIT_SUCCESS;
494 	cleanup = ST_TEST_DATA;
495 	comp_perf_options_default(test_data);
496 
497 	if (comp_perf_options_parse(test_data, argc, argv) < 0) {
498 		RTE_LOG(ERR, USER1,
499 			"Parsing one or more user options failed\n");
500 		ret = EXIT_FAILURE;
501 		goto end;
502 	}
503 
504 	if (comp_perf_options_check(test_data) < 0) {
505 		ret = EXIT_FAILURE;
506 		goto end;
507 	}
508 
509 	if (comp_perf_initialize_compressdev(test_data) < 0) {
510 		ret = EXIT_FAILURE;
511 		goto end;
512 	}
513 
514 	cleanup = ST_COMPDEV;
515 	if (comp_perf_dump_input_data(test_data) < 0) {
516 		ret = EXIT_FAILURE;
517 		goto end;
518 	}
519 
520 	cleanup = ST_INPUT_DATA;
521 	if (comp_perf_allocate_memory(test_data) < 0) {
522 		ret = EXIT_FAILURE;
523 		goto end;
524 	}
525 
526 	if (prepare_bufs(test_data) < 0) {
527 		ret = EXIT_FAILURE;
528 		goto end;
529 	}
530 
531 	if (test_data->level.inc != 0)
532 		level = test_data->level.min;
533 	else
534 		level = test_data->level.list[0];
535 
536 	printf("Burst size = %u\n", test_data->burst_sz);
537 	printf("File size = %zu\n", test_data->input_data_sz);
538 
539 	printf("%6s%12s%17s%19s%21s%15s%21s%23s%16s\n",
540 		"Level", "Comp size", "Comp ratio [%]",
541 		"Comp [Cycles/it]", "Comp [Cycles/Byte]", "Comp [Gbps]",
542 		"Decomp [Cycles/it]", "Decomp [Cycles/Byte]", "Decomp [Gbps]");
543 
544 	cleanup = ST_DURING_TEST;
545 	while (level <= test_data->level.max) {
546 
547 		/*
548 		 * Run a first iteration, to verify compression and
549 		 * get the compression ratio for the level
550 		 */
551 		if (cperf_verification(test_data, level) != EXIT_SUCCESS)
552 			break;
553 
554 		/*
555 		 * Run benchmarking test
556 		 */
557 		if (cperf_benchmark(test_data, level) != EXIT_SUCCESS)
558 			break;
559 
560 		printf("%6u%12zu%17.2f%19"PRIu64"%21.2f"
561 					"%15.2f%21"PRIu64"%23.2f%16.2f\n",
562 		       level, test_data->comp_data_sz, test_data->ratio,
563 		       test_data->comp_tsc_duration[level],
564 		       test_data->comp_tsc_byte, test_data->comp_gbps,
565 		       test_data->decomp_tsc_duration[level],
566 		       test_data->decomp_tsc_byte, test_data->decomp_gbps);
567 
568 		if (test_data->level.inc != 0)
569 			level += test_data->level.inc;
570 		else {
571 			if (++level_idx == test_data->level.count)
572 				break;
573 			level = test_data->level.list[level_idx];
574 		}
575 	}
576 
577 end:
578 	switch (cleanup) {
579 
580 	case ST_DURING_TEST:
581 	case ST_PREPARE_BUF:
582 		free_bufs(test_data);
583 		/* fallthrough */
584 	case ST_MEMORY_ALLOC:
585 		rte_free(test_data->decomp_bufs);
586 		rte_free(test_data->comp_bufs);
587 		rte_free(test_data->decompressed_data);
588 		rte_free(test_data->compressed_data);
589 		rte_mempool_free(test_data->op_pool);
590 		rte_mempool_free(test_data->decomp_buf_pool);
591 		rte_mempool_free(test_data->comp_buf_pool);
592 		/* fallthrough */
593 	case ST_INPUT_DATA:
594 		rte_free(test_data->input_data);
595 		/* fallthrough */
596 	case ST_COMPDEV:
597 		if (test_data->cdev_id != -1)
598 			rte_compressdev_stop(test_data->cdev_id);
599 		/* fallthrough */
600 	case ST_TEST_DATA:
601 		rte_free(test_data);
602 		/* fallthrough */
603 	case ST_CLEAR:
604 	default:
605 		i = rte_eal_cleanup();
606 		if (i) {
607 			RTE_LOG(ERR, USER1,
608 				"Error from rte_eal_cleanup(), %d\n", i);
609 			ret = i;
610 		}
611 		break;
612 	}
613 	return ret;
614 }
615