API Reference Manual  1.46.0
odp_crc.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2021 Nokia
3  */
4 
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdint.h>
16 #include <inttypes.h>
17 #include <getopt.h>
18 
19 #include <odp_api.h>
20 #include <odp/helper/odph_api.h>
21 
22 #define KB 1024ull
23 #define MB (1024ull * 1024ull)
24 
25 /* Command line options */
26 typedef struct {
27  uint32_t size;
28  uint32_t rounds;
29  uint32_t offset;
30  uint32_t test;
31 } options_t;
32 
33 static options_t options;
34 static const options_t options_def = {
35  .size = 16,
36  .rounds = 10000,
37  .offset = 0,
38  .test = 0,
39 };
40 
41 static void print_usage(void)
42 {
43  printf("\n"
44  "CRC performance test\n"
45  "\n"
46  "Usage: odp_crc_perf [options]\n"
47  "\n"
48  " -s, --size Size of buffer in KB (default %u)\n"
49  " -r, --rounds Number of test rounds (default %u)\n"
50  " Rounded down to nearest multiple of 8\n"
51  " -o, --offset Offset of data (default %u)\n"
52  " -t, --test Which API to test (default %u)\n"
53  " 0: both\n"
54  " 1: odp_hash_crc32c\n"
55  " 2: odp_hash_crc32\n"
56  " -h, --help This help\n"
57  "\n",
58  options_def.size, options_def.rounds, options_def.offset,
59  options.test);
60 }
61 
62 static int parse_options(int argc, char *argv[])
63 {
64  int opt;
65  int ret = 0;
66 
67  static const struct option longopts[] = {
68  { "size", required_argument, NULL, 's' },
69  { "rounds", required_argument, NULL, 'r' },
70  { "offset", required_argument, NULL, 'o' },
71  { "test", required_argument, NULL, 't' },
72  { "help", no_argument, NULL, 'h' },
73  { NULL, 0, NULL, 0 }
74  };
75 
76  static const char *shortopts = "+s:r:o:t:h";
77 
78  options = options_def;
79 
80  while (1) {
81  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
82 
83  if (opt == -1)
84  break;
85 
86  switch (opt) {
87  case 's':
88  options.size = atol(optarg);
89  break;
90  case 'r':
91  options.rounds = atol(optarg);
92  break;
93  case 'o':
94  options.offset = atol(optarg);
95  break;
96  case 't':
97  options.test = atol(optarg);
98  break;
99  case 'h':
100  /* fall through */
101  default:
102  print_usage();
103  ret = -1;
104  break;
105  }
106  }
107 
108  if (options.size < 1) {
109  ODPH_ERR("Invalid size: %" PRIu32 "\n", options.size);
110  return -1;
111  }
112 
113  if (options.offset > 4 * KB) {
114  ODPH_ERR("Invalid offset: %" PRIu32 "\n", options.offset);
115  return -1;
116  }
117 
118  if (options.test > 2) {
119  ODPH_ERR("Invalid API to test: %" PRIu32 "\n", options.test);
120  return -1;
121  }
122 
123  return ret;
124 }
125 
126 static void report(uint64_t nsec)
127 {
128  uint64_t size = (uint64_t)options.size * KB;
129  uint32_t rounds = options.rounds & ~7ul;
130  double mb, seconds;
131 
132  printf("size: %d KB rounds: %d offset: %d ",
133  options.size, rounds, options.offset);
134  mb = (double)(size * (uint64_t)rounds) / (double)MB;
135  seconds = (double)nsec / (double)ODP_TIME_SEC_IN_NS;
136  printf("MB: %.3f seconds: %.3f ", mb, seconds);
137  printf("MB/s: %.3f", mb / seconds);
138  printf("\n\n");
139 }
140 
141 static uint64_t measure_crc32c(uint8_t *data, uint32_t size)
142 {
143  void *p = data + options.offset;
144  uint32_t crc = 1;
145  volatile uint32_t v;
146  odp_time_t start = odp_time_local();
147 
148  for (uint32_t i = 0; i < options.rounds / 8; i++) {
149  crc ^= odp_hash_crc32c(p, size, crc);
150  crc ^= odp_hash_crc32c(p, size, crc);
151  crc ^= odp_hash_crc32c(p, size, crc);
152  crc ^= odp_hash_crc32c(p, size, crc);
153 
154  crc ^= odp_hash_crc32c(p, size, crc);
155  crc ^= odp_hash_crc32c(p, size, crc);
156  crc ^= odp_hash_crc32c(p, size, crc);
157  crc ^= odp_hash_crc32c(p, size, crc);
158  }
159 
160  /* Make sure that crc is not optimized out. */
161  v = crc;
162 
163  /* Quell "unused" warning. */
164  (void)v;
165 
166  return odp_time_diff_ns(odp_time_local(), start);
167 }
168 
169 static void test_odp_hash_crc32c(uint8_t *data)
170 {
171  uint64_t size = (uint64_t)options.size * KB;
172  uint64_t nsec;
173 
174  /* Warm-up. */
175  measure_crc32c(data, size);
176 
177  /* Actual measurement. */
178  nsec = measure_crc32c(data, size);
179 
180  report(nsec);
181 }
182 
183 static uint64_t measure_crc32(uint8_t *data, uint32_t size)
184 {
185  void *p = data + options.offset;
186  uint32_t crc = 1;
187  volatile uint32_t v;
188  odp_time_t start = odp_time_local();
189 
190  for (uint32_t i = 0; i < options.rounds / 8; i++) {
191  crc ^= odp_hash_crc32(p, size, crc);
192  crc ^= odp_hash_crc32(p, size, crc);
193  crc ^= odp_hash_crc32(p, size, crc);
194  crc ^= odp_hash_crc32(p, size, crc);
195 
196  crc ^= odp_hash_crc32(p, size, crc);
197  crc ^= odp_hash_crc32(p, size, crc);
198  crc ^= odp_hash_crc32(p, size, crc);
199  crc ^= odp_hash_crc32(p, size, crc);
200  }
201 
202  /* Make sure that crc is not optimized out. */
203  v = crc;
204 
205  /* Quell "unused" warning. */
206  (void)v;
207 
208  return odp_time_diff_ns(odp_time_local(), start);
209 }
210 
211 static void test_odp_hash_crc32(uint8_t *data)
212 {
213  uint64_t size = (uint64_t)options.size * KB;
214  uint64_t nsec;
215 
216  /* Warm-up. */
217  measure_crc32(data, size);
218 
219  /* Actual measurement. */
220  nsec = measure_crc32(data, size);
221 
222  report(nsec);
223 }
224 
225 int main(int argc, char **argv)
226 {
227  odp_instance_t instance;
228  odp_init_t init;
229 
230  if (parse_options(argc, argv))
231  exit(EXIT_FAILURE);
232 
233  /* List features not to be used */
234  odp_init_param_init(&init);
235  init.not_used.feat.cls = 1;
236  init.not_used.feat.compress = 1;
237  init.not_used.feat.crypto = 1;
238  init.not_used.feat.ipsec = 1;
239  init.not_used.feat.schedule = 1;
240  init.not_used.feat.stash = 1;
241  init.not_used.feat.timer = 1;
242  init.not_used.feat.tm = 1;
243 
244  /* Init ODP before calling anything else */
245  if (odp_init_global(&instance, &init, NULL)) {
246  ODPH_ERR("Global init failed.\n");
247  exit(EXIT_FAILURE);
248  }
249 
250  /* Init this thread */
251  if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
252  ODPH_ERR("Local init failed.\n");
253  exit(EXIT_FAILURE);
254  }
255 
257 
258  uint8_t *buf, *data;
259  uint32_t size = options.size * KB;
260  uint64_t seed = 1;
261  const unsigned long page = 4 * KB;
262 
263  /* One extra page for alignment, another one for offset. */
264  buf = (uint8_t *)malloc(size + page * 2);
265 
266  if (!buf) {
267  ODPH_ERR("Memory allocation failed.\n");
268  exit(EXIT_FAILURE);
269  }
270 
271  /* Align to start of page. */
272  data = (uint8_t *)(((uintptr_t)buf + (page - 1)) & ~(page - 1));
273 
274  if (odp_random_test_data(data, size, &seed) != (int32_t)size) {
275  ODPH_ERR("odp_random_test_data() failed.\n");
276  exit(EXIT_FAILURE);
277  }
278 
279  if (options.test == 0 || options.test == 1) {
280  printf("odp_hash_crc32c\n"
281  "---------------\n");
282  test_odp_hash_crc32c(data);
283  }
284 
285  if (options.test == 0 || options.test == 2) {
286  printf("odp_hash_crc32\n"
287  "--------------\n");
288  test_odp_hash_crc32(data);
289  }
290 
291  free(buf);
292 
293  if (odp_term_local()) {
294  ODPH_ERR("Local terminate failed.\n");
295  exit(EXIT_FAILURE);
296  }
297 
298  if (odp_term_global(instance)) {
299  ODPH_ERR("Global terminate failed.\n");
300  exit(EXIT_FAILURE);
301  }
302 
303  return 0;
304 }
uint32_t odp_hash_crc32(const void *data, uint32_t data_len, uint32_t init_val)
Calculate CRC-32.
uint32_t odp_hash_crc32c(const void *data, uint32_t data_len, uint32_t init_val)
Calculate CRC-32C.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
int32_t odp_random_test_data(uint8_t *buf, uint32_t len, uint64_t *seed)
Generate repeatable random data for testing purposes.
void odp_sys_info_print(void)
Print system info.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_feature_t not_used
Unused features.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t stash
Stash APIs, e.g., odp_stash_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
uint32_t schedule
Scheduler APIs, e.g., odp_schedule_xxx()
struct odp_feature_t::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()