aboutsummaryrefslogtreecommitdiff
path: root/test/performance/odp_crc.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/performance/odp_crc.c')
-rw-r--r--test/performance/odp_crc.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/test/performance/odp_crc.c b/test/performance/odp_crc.c
new file mode 100644
index 000000000..1b631c691
--- /dev/null
+++ b/test/performance/odp_crc.c
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2021 Nokia
+ */
+
+/**
+ * @example odp_crc.c
+ *
+ * Performance test application for CRC hash APIs
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#define KB 1024ull
+#define MB (1024ull * 1024ull)
+
+/* Command line options */
+typedef struct {
+ uint32_t size;
+ uint32_t rounds;
+ uint32_t offset;
+ uint32_t test;
+} options_t;
+
+static options_t options;
+static const options_t options_def = {
+ .size = 16,
+ .rounds = 10000,
+ .offset = 0,
+ .test = 0,
+};
+
+static void print_usage(void)
+{
+ printf("\n"
+ "CRC performance test\n"
+ "\n"
+ "Usage: odp_crc_perf [options]\n"
+ "\n"
+ " -s, --size Size of buffer in KB (default %u)\n"
+ " -r, --rounds Number of test rounds (default %u)\n"
+ " Rounded down to nearest multiple of 8\n"
+ " -o, --offset Offset of data (default %u)\n"
+ " -t, --test Which API to test (default %u)\n"
+ " 0: both\n"
+ " 1: odp_hash_crc32c\n"
+ " 2: odp_hash_crc32\n"
+ " -h, --help This help\n"
+ "\n",
+ options_def.size, options_def.rounds, options_def.offset,
+ options.test);
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int opt;
+ int long_index;
+ int ret = 0;
+
+ static const struct option longopts[] = {
+ { "size", required_argument, NULL, 's' },
+ { "rounds", required_argument, NULL, 'r' },
+ { "offset", required_argument, NULL, 'o' },
+ { "test", required_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const char *shortopts = "+s:r:o:t:h";
+
+ options = options_def;
+
+ while (1) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 's':
+ options.size = atol(optarg);
+ break;
+ case 'r':
+ options.rounds = atol(optarg);
+ break;
+ case 'o':
+ options.offset = atol(optarg);
+ break;
+ case 't':
+ options.test = atol(optarg);
+ break;
+ case 'h':
+ /* fall through */
+ default:
+ print_usage();
+ ret = -1;
+ break;
+ }
+ }
+
+ if (options.size < 1) {
+ ODPH_ERR("Invalid size: %" PRIu32 "\n", options.size);
+ return -1;
+ }
+
+ if (options.offset > 4 * KB) {
+ ODPH_ERR("Invalid offset: %" PRIu32 "\n", options.offset);
+ return -1;
+ }
+
+ if (options.test > 2) {
+ ODPH_ERR("Invalid API to test: %" PRIu32 "\n", options.test);
+ return -1;
+ }
+
+ return ret;
+}
+
+static void report(uint64_t nsec)
+{
+ uint64_t size = (uint64_t)options.size * KB;
+ uint32_t rounds = options.rounds & ~7ul;
+ double mb, seconds;
+
+ printf("size: %d KB rounds: %d offset: %d ",
+ options.size, rounds, options.offset);
+ mb = (double)(size * (uint64_t)rounds) / (double)MB;
+ seconds = (double)nsec / (double)ODP_TIME_SEC_IN_NS;
+ printf("MB: %.3f seconds: %.3f ", mb, seconds);
+ printf("MB/s: %.3f", mb / seconds);
+ printf("\n\n");
+}
+
+static uint64_t measure_crc32c(uint8_t *data, uint32_t size)
+{
+ void *p = data + options.offset;
+ uint32_t crc = 1;
+ volatile uint32_t v;
+ odp_time_t start = odp_time_local();
+
+ for (uint32_t i = 0; i < options.rounds / 8; i++) {
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ crc ^= odp_hash_crc32c(p, size, crc);
+ }
+
+ /* Make sure that crc is not optimized out. */
+ v = crc;
+
+ /* Quell "unused" warning. */
+ (void)v;
+
+ return odp_time_diff_ns(odp_time_local(), start);
+}
+
+static void test_odp_hash_crc32c(uint8_t *data)
+{
+ uint64_t size = (uint64_t)options.size * KB;
+ uint64_t nsec;
+
+ /* Warm-up. */
+ measure_crc32c(data, size);
+
+ /* Actual measurement. */
+ nsec = measure_crc32c(data, size);
+
+ report(nsec);
+}
+
+static uint64_t measure_crc32(uint8_t *data, uint32_t size)
+{
+ void *p = data + options.offset;
+ uint32_t crc = 1;
+ volatile uint32_t v;
+ odp_time_t start = odp_time_local();
+
+ for (uint32_t i = 0; i < options.rounds / 8; i++) {
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ crc ^= odp_hash_crc32(p, size, crc);
+ }
+
+ /* Make sure that crc is not optimized out. */
+ v = crc;
+
+ /* Quell "unused" warning. */
+ (void)v;
+
+ return odp_time_diff_ns(odp_time_local(), start);
+}
+
+static void test_odp_hash_crc32(uint8_t *data)
+{
+ uint64_t size = (uint64_t)options.size * KB;
+ uint64_t nsec;
+
+ /* Warm-up. */
+ measure_crc32(data, size);
+
+ /* Actual measurement. */
+ nsec = measure_crc32(data, size);
+
+ report(nsec);
+}
+
+int main(int argc, char **argv)
+{
+ odp_instance_t instance;
+ odp_init_t init;
+
+ if (parse_options(argc, argv))
+ exit(EXIT_FAILURE);
+
+ /* List features not to be used */
+ odp_init_param_init(&init);
+ init.not_used.feat.cls = 1;
+ init.not_used.feat.compress = 1;
+ init.not_used.feat.crypto = 1;
+ init.not_used.feat.ipsec = 1;
+ init.not_used.feat.schedule = 1;
+ init.not_used.feat.stash = 1;
+ init.not_used.feat.timer = 1;
+ init.not_used.feat.tm = 1;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init, NULL)) {
+ ODPH_ERR("Global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_sys_info_print();
+
+ uint8_t *buf, *data;
+ uint32_t size = options.size * KB;
+ uint64_t seed = 1;
+ const unsigned long page = 4 * KB;
+
+ /* One extra page for alignment, another one for offset. */
+ buf = (uint8_t *)malloc(size + page * 2);
+
+ if (!buf) {
+ ODPH_ERR("Memory allocation failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Align to start of page. */
+ data = (uint8_t *)(((uintptr_t)buf + (page - 1)) & ~(page - 1));
+
+ if (odp_random_test_data(data, size, &seed) != (int32_t)size) {
+ ODPH_ERR("odp_random_test_data() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (options.test == 0 || options.test == 1) {
+ printf("odp_hash_crc32c\n"
+ "---------------\n");
+ test_odp_hash_crc32c(data);
+ }
+
+ if (options.test == 0 || options.test == 2) {
+ printf("odp_hash_crc32\n"
+ "--------------\n");
+ test_odp_hash_crc32(data);
+ }
+
+ free(buf);
+
+ if (odp_term_local()) {
+ ODPH_ERR("Local terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Global terminate failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}