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