[PATCH] Add new benchmarking utility, bench-cycles
Jussi Kivilinna
jussi.kivilinna at iki.fi
Mon Oct 28 10:58:43 CET 2013
* tests/Makefile.am (TESTS): Add 'bench-slope'.
* tests/bench-slope.c: New.
--
Bench-slope is new benchmarking tool for libgcrypt for obtaining overheadless
cycles/byte speed of cipher and hash algorithms. Tool measures the time each
operation (hash/encrypt/decrypt/authentication) takes for different buffer
sizes of from ~0kB to ~4kB and calculates the slope for these data points.
The default output is then given as nanosecs/byte and mebibytes/sec. If user
provides the speed of used CPU, tool also outputs cycles/byte result (CPU-Ghz *
ns/B = c/B).
Output without CPU speed (with ARM Cortex-A8):
$ tests/bench-slope hash
Hash:
| nanosecs/byte mebibytes/sec cycles/byte
MD5 | 7.35 ns/B 129.7 MiB/s - c/B
SHA1 | 12.30 ns/B 77.53 MiB/s - c/B
RIPEMD160 | 15.96 ns/B 59.77 MiB/s - c/B
TIGER192 | 55.55 ns/B 17.17 MiB/s - c/B
SHA256 | 24.38 ns/B 39.12 MiB/s - c/B
SHA384 | 34.24 ns/B 27.86 MiB/s - c/B
SHA512 | 34.19 ns/B 27.90 MiB/s - c/B
SHA224 | 24.38 ns/B 39.12 MiB/s - c/B
MD4 | 5.68 ns/B 168.0 MiB/s - c/B
CRC32 | 9.26 ns/B 103.0 MiB/s - c/B
CRC32RFC1510 | 9.20 ns/B 103.6 MiB/s - c/B
CRC24RFC2440 | 87.31 ns/B 10.92 MiB/s - c/B
WHIRLPOOL | 253.3 ns/B 3.77 MiB/s - c/B
TIGER | 55.55 ns/B 17.17 MiB/s - c/B
TIGER2 | 55.55 ns/B 17.17 MiB/s - c/B
GOSTR3411_94 | 212.0 ns/B 4.50 MiB/s - c/B
STRIBOG256 | 630.1 ns/B 1.51 MiB/s - c/B
STRIBOG512 | 630.1 ns/B 1.51 MiB/s - c/B
=
With CPU speed (with Intel i5-4570, 3.2Ghz when turbo-boost disabled):
$ tests/bench-slope --cpu-mhz 3201 cipher arcfour blowfish aes
Cipher:
ARCFOUR | nanosecs/byte mebibytes/sec cycles/byte
STREAM enc | 2.43 ns/B 392.1 MiB/s 7.79 c/B
STREAM dec | 2.44 ns/B 390.2 MiB/s 7.82 c/B
=
BLOWFISH | nanosecs/byte mebibytes/sec cycles/byte
ECB enc | 7.62 ns/B 125.2 MiB/s 24.38 c/B
ECB dec | 7.63 ns/B 125.0 MiB/s 24.43 c/B
CBC enc | 9.18 ns/B 103.9 MiB/s 29.38 c/B
CBC dec | 2.60 ns/B 366.2 MiB/s 8.34 c/B
CFB enc | 9.17 ns/B 104.0 MiB/s 29.35 c/B
CFB dec | 2.66 ns/B 358.1 MiB/s 8.53 c/B
OFB enc | 8.97 ns/B 106.3 MiB/s 28.72 c/B
OFB dec | 8.97 ns/B 106.3 MiB/s 28.71 c/B
CTR enc | 2.60 ns/B 366.5 MiB/s 8.33 c/B
CTR dec | 2.60 ns/B 367.1 MiB/s 8.32 c/B
=
AES | nanosecs/byte mebibytes/sec cycles/byte
ECB enc | 0.439 ns/B 2173.0 MiB/s 1.40 c/B
ECB dec | 0.489 ns/B 1949.5 MiB/s 1.57 c/B
CBC enc | 1.64 ns/B 580.8 MiB/s 5.26 c/B
CBC dec | 0.219 ns/B 4357.6 MiB/s 0.701 c/B
CFB enc | 1.53 ns/B 623.6 MiB/s 4.90 c/B
CFB dec | 0.219 ns/B 4350.5 MiB/s 0.702 c/B
OFB enc | 1.51 ns/B 629.9 MiB/s 4.85 c/B
OFB dec | 1.51 ns/B 629.9 MiB/s 4.85 c/B
CTR enc | 0.288 ns/B 3308.5 MiB/s 0.923 c/B
CTR dec | 0.288 ns/B 3316.9 MiB/s 0.920 c/B
CCM enc | 1.93 ns/B 493.8 MiB/s 6.18 c/B
CCM dec | 1.93 ns/B 494.0 MiB/s 6.18 c/B
CCM auth | 1.64 ns/B 580.1 MiB/s 5.26 c/B
=
Note: It's highly recommented to disable turbo-boost and dynamic CPU frequency
features when making these kind of measurements to reduce variance.
Note: The results are maximum performance for each operation; the actual speed
in application depends on various matters, such as: used buffer sizes, cache
usage, etc.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
tests/Makefile.am | 4
tests/bench-slope.c | 1154 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1156 insertions(+), 2 deletions(-)
create mode 100644 tests/bench-slope.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ac84e75..c9ba5f4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -24,8 +24,8 @@ TESTS = version mpitests tsexp t-convert \
fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 t-ed25519
-# The last test to run.
-TESTS += benchmark
+# The last tests to run.
+TESTS += benchmark bench-slope
# Need to include ../src in addition to top_srcdir because gcrypt.h is
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
new file mode 100644
index 0000000..b706d37
--- /dev/null
+++ b/tests/bench-slope.c
@@ -0,0 +1,1154 @@
+/* bench-slope.c - for libgcrypt
+ * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <time.h>
+
+#ifdef _GCRYPT_IN_LIBGCRYPT
+#include "../src/gcrypt-int.h"
+#include "../compat/libcompat.h"
+#else
+#include <gcrypt.h>
+#endif
+
+#define PGM "bench-slope"
+
+static int verbose;
+
+
+/* CPU Ghz value provided by user, allows constructing cycles/byte and other
+ results. */
+static double cpu_ghz = -1;
+
+
+
+/*************************************** Default parameters for measurements. */
+
+/* Start at small buffer size, to get reasonably timer calibration for fast
+ * implementations (AES-NI etc). Sixteen selected to support the largest block
+ * size of current cipher block. */
+#define BUF_START_SIZE 16
+
+/* From ~0 to ~4kbytes give comparable results with results from academia
+ * (SUPERCOP). */
+#define BUF_END_SIZE (BUF_START_SIZE + 4096)
+
+/* With 128 byte steps, we get (4096)/128 = 32 data points. */
+#define BUF_STEP_SIZE 128
+
+/* Number of repeated measurements at each data point. The median of these
+ * measurements is selected as data point further analysis. */
+#define NUM_MEASUREMENT_REPETITIONS 32
+
+/**************************************************** High-resolution timers. */
+
+/* This benchmarking module needs needs high resolution timer. */
+#undef NO_GET_NSEC_TIME
+#if defined(_WIN32)
+struct nsec_time
+{
+ LARGE_INTEGER perf_count;
+};
+
+static void
+get_nsec_time (struct nsec_time *t)
+{
+ BOOL ok;
+
+ ok = QueryPerformanceCounter (&t->perf_count);
+ assert (ok);
+}
+
+static double
+get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
+{
+ static double nsecs_per_count = 0.0;
+ double nsecs;
+
+ if (nsecs_per_count == 0.0)
+ {
+ LARGE_INTEGER perf_freq;
+ BOOL ok;
+
+ /* Get counts per second. */
+ ok = QueryPerformanceFrequency (&perf_freq);
+ assert (ok);
+
+ nsecs_per_count = 1.0 / perf_freq.QuadPart;
+ nsecs_per_count *= 1000000.0 * 1000.0; /* sec => nsec */
+
+ assert (nsecs_per_count > 0.0);
+ }
+
+ nsecs = end->perf_count.QuadPart - start->perf_count.QuadPart; /* counts */
+ nsecs *= nsecs_per_count; /* counts * (nsecs / count) => nsecs */
+
+ return nsecs;
+}
+#elif defined(HAVE_CLOCK_GETTIME)
+struct nsec_time
+{
+ struct timespec ts;
+};
+
+static void
+get_nsec_time (struct nsec_time *t)
+{
+ int err;
+
+ err = clock_gettime (CLOCK_REALTIME, &t->ts);
+ assert (err == 0);
+}
+
+static double
+get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
+{
+ double nsecs;
+
+ nsecs = end->ts.tv_sec - start->ts.tv_sec;
+ nsecs *= 1000000.0 * 1000.0; /* sec => nsec */
+
+ /* This way we don't have to care if tv_nsec unsigned or signed. */
+ if (end->ts.tv_nsec >= start->ts.tv_nsec)
+ nsecs += end->ts.tv_nsec - start->ts.tv_nsec;
+ else
+ nsecs -= start->ts.tv_nsec - end->ts.tv_nsec;
+
+ return nsecs;
+}
+#elif defined(HAVE_GETTIMEOFDAY)
+struct nsec_time
+{
+ struct timeval tv;
+};
+
+static void
+get_nsec_time (struct nsec_time *t)
+{
+ int err;
+
+ err = gettimeofday (&t->tv, NULL);
+ assert (err == 0);
+}
+
+static double
+get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
+{
+ double nsecs;
+
+ nsecs = end->tv.tv_sec - start->tv.tv_sec;
+ nsecs *= 1000000; /* sec => µsec */
+
+ /* This way we don't have to care if tv_usec unsigned or signed. */
+ if (end->tv.tv_usec >= start->tv.tv_usec)
+ nsecs += end->tv.tv_usec - start->tv.tv_usec;
+ else
+ nsecs -= start->tv.tv_usec - end->tv.tv_usec;
+
+ nsecs *= 1000; /* µsec => nsec */
+
+ return nsecs;
+}
+#else
+#define NO_GET_NSEC_TIME 1
+#endif
+
+
+/* If no high resolution timer found, provide dummy bench-slope. */
+#ifdef NO_GET_NSEC_TIME
+
+
+int
+main (void)
+{
+ /* do nothing */
+ return 0;
+}
+
+
+#else /* !NO_GET_NSEC_TIME */
+
+
+/********************************************** Slope benchmarking framework. */
+
+struct bench_obj
+{
+ const struct bench_ops *ops;
+
+ unsigned int num_measure_repetitions;
+ unsigned int min_bufsize;
+ unsigned int max_bufsize;
+ unsigned int step_size;
+
+ void *priv;
+};
+
+typedef int (*const bench_initialize_t) (struct bench_obj * obj);
+typedef void (*const bench_finalize_t) (struct bench_obj * obj);
+typedef void (*const bench_do_run_t) (struct bench_obj * obj, void *buffer,
+ size_t buflen);
+
+struct bench_ops
+{
+ bench_initialize_t initialize;
+ bench_finalize_t finalize;
+ bench_do_run_t do_run;
+};
+
+
+double
+get_slope (double (*const get_x) (unsigned int idx, void *priv),
+ void *get_x_priv, double y_points[], unsigned int npoints,
+ double *overhead)
+{
+ double sumx, sumy, sumx2, sumy2, sumxy;
+ unsigned int i;
+ double b, a;
+
+ sumx = sumy = sumx2 = sumy2 = sumxy = 0;
+
+ for (i = 0; i < npoints; i++)
+ {
+ double x, y;
+
+ x = get_x (i, get_x_priv); /* bytes */
+ y = y_points[i]; /* nsecs */
+
+ sumx += x;
+ sumy += y;
+ sumx2 += x * x;
+ //sumy2 += y * y;
+ sumxy += x * y;
+ }
+
+ b = (npoints * sumxy - sumx * sumy) / (npoints * sumx2 - sumx * sumx);
+ a = (sumy - b * sumx) / npoints;
+
+ if (overhead)
+ *overhead = a; /* nsecs */
+
+ return b; /* nsecs per byte */
+}
+
+
+double
+get_bench_obj_point_x (unsigned int idx, void *priv)
+{
+ struct bench_obj *obj = priv;
+ return (double) (obj->min_bufsize + (idx * obj->step_size));
+}
+
+
+unsigned int
+get_num_measurements (struct bench_obj *obj)
+{
+ unsigned int buf_range = obj->max_bufsize - obj->min_bufsize;
+ unsigned int num = buf_range / obj->step_size + 1;
+
+ while (obj->min_bufsize + (num * obj->step_size) > obj->max_bufsize)
+ num--;
+
+ return num + 1;
+}
+
+
+static int
+double_cmp (const void *__a, const void *__b)
+{
+ const double *a, *b;
+
+ a = __a;
+ b = __b;
+
+ if (*a > *b)
+ return 1;
+ if (*a < *b)
+ return -1;
+ return 0;
+}
+
+
+double
+do_bench_obj_measurement (struct bench_obj *obj, void *buffer, size_t buflen,
+ unsigned int loop_iterations)
+{
+ const unsigned int num_repetitions = obj->num_measure_repetitions;
+ const bench_do_run_t do_run = obj->ops->do_run;
+ double measurement_raw[num_repetitions];
+ struct nsec_time start, end;
+ unsigned int rep, loop;
+ double res;
+
+ if (num_repetitions < 1 || loop_iterations < 1)
+ return 0.0;
+
+ for (rep = 0; rep < num_repetitions; rep++)
+ {
+ get_nsec_time (&start);
+
+ for (loop = 0; loop < loop_iterations; loop++)
+ do_run (obj, buffer, buflen);
+
+ get_nsec_time (&end);
+
+ measurement_raw[rep] = get_time_nsec_diff (&start, &end);
+ }
+
+ /* Return median of repeated measurements. */
+ qsort (measurement_raw, num_repetitions, sizeof (measurement_raw[0]),
+ double_cmp);
+
+ if (num_repetitions % 2 == 1)
+ return measurement_raw[num_repetitions / 2];
+
+ res = measurement_raw[num_repetitions / 2]
+ + measurement_raw[num_repetitions / 2 - 1];
+ return res / 2;
+}
+
+
+unsigned int
+adjust_loop_iterations_to_timer_accuracy (struct bench_obj *obj, void *buffer)
+{
+ const double increase_thres = 3.0;
+ double tmp, nsecs;
+ unsigned int loop_iterations;
+ unsigned int test_bufsize;
+
+ test_bufsize = obj->min_bufsize;
+ if (test_bufsize == 0)
+ test_bufsize += obj->step_size;
+
+ loop_iterations = 0;
+ do
+ {
+ /* Increase loop iterations until we get other results than zero. */
+ nsecs =
+ do_bench_obj_measurement (obj, buffer, test_bufsize,
+ ++loop_iterations);
+ }
+ while (nsecs < 1.0 - 0.1);
+ do
+ {
+ /* Increase loop iterations until we get reasonable increase for elapsed time. */
+ tmp =
+ do_bench_obj_measurement (obj, buffer, test_bufsize,
+ ++loop_iterations);
+ }
+ while (tmp < nsecs * (increase_thres - 0.1));
+
+ return loop_iterations;
+}
+
+
+/* Benchmark and return linear regression slope in nanoseconds per byte. */
+double
+do_slope_benchmark (struct bench_obj *obj)
+{
+ unsigned int num_measurements;
+ double *measurements = NULL;
+ double slope, overhead;
+ unsigned int loop_iterations, midx, i;
+ unsigned char *real_buffer = NULL;
+ unsigned char *buffer;
+ size_t cur_bufsize;
+ int err;
+
+ err = obj->ops->initialize (obj);
+ if (err < 0)
+ return -1;
+
+ num_measurements = get_num_measurements (obj);
+ measurements = calloc (num_measurements, sizeof (*measurements));
+ if (!measurements)
+ goto err_free;
+
+ if (num_measurements < 1 || obj->num_measure_repetitions < 1 ||
+ obj->max_bufsize < 1 || obj->min_bufsize > obj->max_bufsize)
+ goto err_free;
+
+ real_buffer = malloc (obj->max_bufsize + 128);
+ if (!real_buffer)
+ goto err_free;
+ /* Get aligned buffer */
+ buffer = real_buffer;
+ buffer += 128 - ((real_buffer - (unsigned char *) 0) & (128 - 1));
+
+ for (i = 0; i < obj->max_bufsize; i++)
+ buffer[i] = 0x55 ^ (-i);
+
+ /* Adjust number of loop iterations up to timer accuracy. */
+ loop_iterations = adjust_loop_iterations_to_timer_accuracy (obj, buffer);
+
+ /* Perform measurements */
+ for (midx = 0, cur_bufsize = obj->min_bufsize;
+ cur_bufsize <= obj->max_bufsize; cur_bufsize += obj->step_size, midx++)
+ {
+ measurements[midx] =
+ do_bench_obj_measurement (obj, buffer, cur_bufsize, loop_iterations);
+ measurements[midx] /= loop_iterations;
+ }
+
+ assert (midx == num_measurements);
+
+ slope =
+ get_slope (&get_bench_obj_point_x, obj, measurements, num_measurements,
+ &overhead);
+
+ free (real_buffer);
+ obj->ops->finalize (obj);
+
+ return slope;
+
+err_free:
+ if (measurements)
+ free (measurements);
+ if (real_buffer)
+ free (real_buffer);
+ obj->ops->finalize (obj);
+
+ return -1;
+}
+
+
+/********************************************************** Printing results. */
+
+static void
+double_to_str (char *out, size_t outlen, double value)
+{
+ const char *fmt;
+
+ if (value < 1.0)
+ fmt = "%.3f";
+ else if (value < 100.0)
+ fmt = "%.2f";
+ else
+ fmt = "%.1f";
+
+ snprintf (out, outlen, fmt, value);
+}
+
+static void
+bench_print_result (double nsecs_per_byte)
+{
+ double cycles_per_byte, mbytes_per_sec;
+ char nsecpbyte_buf[16];
+ char mbpsec_buf[16];
+ char cpbyte_buf[16];
+
+ strcpy (cpbyte_buf, "-");
+
+ double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte);
+
+ /* If user didn't provide CPU speed, we cannot show cycles/byte results. */
+ if (cpu_ghz > 0.0)
+ {
+ cycles_per_byte = nsecs_per_byte * cpu_ghz;
+ double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte);
+ }
+
+ mbytes_per_sec =
+ (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024);
+ double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec);
+
+ strncat (nsecpbyte_buf, " ns/B", sizeof(nsecpbyte_buf) - 1);
+ strncat (mbpsec_buf, " MiB/s", sizeof(mbpsec_buf) - 1);
+ strncat (cpbyte_buf, " c/B", sizeof(cpbyte_buf) - 1);
+
+ printf ("%14s %15s %13s\n", nsecpbyte_buf, mbpsec_buf, cpbyte_buf);
+}
+
+static void
+bench_print_header (const char *algo_name)
+{
+ printf (" %-14s | ", algo_name);
+ printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec",
+ "cycles/byte");
+}
+
+static void
+bench_print_footer (void)
+{
+ printf (" %-14s =\n", "");
+}
+
+
+/********************************************************* Cipher benchmarks. */
+
+struct bench_cipher_mode
+{
+ int mode;
+ const char *name;
+ struct bench_ops *ops;
+
+ int algo;
+};
+
+
+static int
+bench_encrypt_init (struct bench_obj *obj)
+{
+ struct bench_cipher_mode *mode = obj->priv;
+ gcry_cipher_hd_t hd;
+ int err, keylen;
+
+ obj->min_bufsize = BUF_START_SIZE;
+ obj->max_bufsize = BUF_END_SIZE;
+ obj->step_size = BUF_STEP_SIZE;
+ obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
+
+ err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0);
+ if (err)
+ {
+ fprintf (stderr, PGM ": error opening cipher `%s'\n",
+ gcry_cipher_algo_name (mode->algo));
+ exit (1);
+ }
+
+ keylen = gcry_cipher_get_algo_keylen (mode->algo);
+ if (keylen)
+ {
+ char key[keylen];
+ int i;
+
+ for (i = 0; i < keylen; i++)
+ key[i] = 0x33 ^ (11 - i);
+
+ err = gcry_cipher_setkey (hd, key, keylen);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_setkey failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+ }
+ else
+ {
+ fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n",
+ gcry_cipher_algo_name (mode->algo));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ obj->priv = hd;
+
+ return 0;
+}
+
+static void
+bench_encrypt_free (struct bench_obj *obj)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+
+ gcry_cipher_close (hd);
+}
+
+static void
+bench_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+ int err;
+
+ err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_encrypt failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+}
+
+static void
+bench_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+ int err;
+
+ err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_encrypt failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+}
+
+static struct bench_ops encrypt_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_encrypt_do_bench
+};
+
+static struct bench_ops decrypt_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_decrypt_do_bench
+};
+
+
+
+static void
+bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+ int err;
+ char tag[8];
+ char nonce[11] = { 0x80, 0x01, };
+ size_t params[3];
+
+ gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+
+ /* Set CCM lengths */
+ params[0] = buflen;
+ params[1] = 0; /*aadlen */
+ params[2] = sizeof (tag);
+ err =
+ gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_ctl failed: %s\n", gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_encrypt failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_gettag (hd, tag, sizeof (tag));
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_gettag failed: %s\n", gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+}
+
+static void
+bench_ccm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+ int err;
+ char tag[8] = { 0, };
+ char nonce[11] = { 0x80, 0x01, };
+ size_t params[3];
+
+ gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+
+ /* Set CCM lengths */
+ params[0] = buflen;
+ params[1] = 0; /*aadlen */
+ params[2] = sizeof (tag);
+ err =
+ gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_ctl failed: %s\n", gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_encrypt failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_checktag (hd, tag, sizeof (tag));
+ if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
+ err = gpg_error (GPG_ERR_NO_ERROR);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_gettag failed: %s\n", gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+}
+
+static void
+bench_ccm_authenticate_do_bench (struct bench_obj *obj, void *buf,
+ size_t buflen)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+ int err;
+ char tag[8] = { 0, };
+ char nonce[11] = { 0x80, 0x01, };
+ size_t params[3];
+ char data = 0xff;
+
+ gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+
+ /* Set CCM lengths */
+ params[0] = sizeof (data); /*datalen */
+ params[1] = buflen; /*aadlen */
+ params[2] = sizeof (tag);
+ err =
+ gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_ctl failed: %s\n", gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_authenticate (hd, buf, buflen);
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_authenticate failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_encrypt failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ err = gcry_cipher_gettag (hd, tag, sizeof (tag));
+ if (err)
+ {
+ fprintf (stderr, "gcry_cipher_gettag failed: %s\n", gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+}
+
+static struct bench_ops ccm_encrypt_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_ccm_encrypt_do_bench
+};
+
+static struct bench_ops ccm_decrypt_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_ccm_decrypt_do_bench
+};
+
+static struct bench_ops ccm_authenticate_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_ccm_authenticate_do_bench
+};
+
+
+static struct bench_cipher_mode cipher_modes[] = {
+ {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
+ {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops},
+ {GCRY_CIPHER_MODE_CBC, "CBC enc", &encrypt_ops},
+ {GCRY_CIPHER_MODE_CBC, "CBC dec", &decrypt_ops},
+ {GCRY_CIPHER_MODE_CFB, "CFB enc", &encrypt_ops},
+ {GCRY_CIPHER_MODE_CFB, "CFB dec", &decrypt_ops},
+ {GCRY_CIPHER_MODE_OFB, "OFB enc", &encrypt_ops},
+ {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops},
+ {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops},
+ {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops},
+ {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
+ {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
+ {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
+ {0},
+};
+
+
+static void
+cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
+{
+ struct bench_cipher_mode mode = *pmode;
+ struct bench_obj obj = { 0 };
+ double result;
+ unsigned int blklen;
+
+ mode.algo = algo;
+
+ /* Check if this mode is ok */
+ blklen = gcry_cipher_get_algo_blklen (algo);
+ if (!blklen)
+ return;
+
+ /* Stream cipher? Only test with ECB. */
+ if (blklen == 1 && mode.mode != GCRY_CIPHER_MODE_ECB)
+ return;
+ if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB)
+ {
+ mode.mode = GCRY_CIPHER_MODE_STREAM;
+ mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec";
+ }
+
+ /* CCM has restrictions for block-size */
+ if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
+ return;
+
+ printf (" %14s | ", mode.name);
+ fflush (stdout);
+
+ obj.ops = mode.ops;
+ obj.priv = &mode;
+
+ result = do_slope_benchmark (&obj);
+
+ bench_print_result (result);
+}
+
+
+static void
+__cipher_bench (int algo)
+{
+ const char *algoname;
+ int i;
+
+ algoname = gcry_cipher_algo_name (algo);
+
+ bench_print_header (algoname);
+
+ for (i = 0; cipher_modes[i].mode; i++)
+ cipher_bench_one (algo, &cipher_modes[i]);
+
+ bench_print_footer ();
+}
+
+
+void
+cipher_bench (char **argv, int argc)
+{
+ int i, algo;
+
+ printf ("Cipher:\n");
+
+ if (argv && argc)
+ {
+ for (i = 0; i < argc; i++)
+ {
+ algo = gcry_cipher_map_name (argv[i]);
+ if (algo)
+ __cipher_bench (algo);
+ }
+ }
+ else
+ {
+ for (i = 1; i < 400; i++)
+ if (!gcry_cipher_test_algo (i))
+ __cipher_bench (i);
+ }
+}
+
+
+/*********************************************************** Hash benchmarks. */
+
+struct bench_hash_mode
+{
+ const char *name;
+ struct bench_ops *ops;
+
+ int algo;
+};
+
+
+static int
+bench_hash_init (struct bench_obj *obj)
+{
+ struct bench_hash_mode *mode = obj->priv;
+ gcry_md_hd_t hd;
+ int err;
+
+ obj->min_bufsize = BUF_START_SIZE;
+ obj->max_bufsize = BUF_END_SIZE;
+ obj->step_size = BUF_STEP_SIZE;
+ obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
+
+ err = gcry_md_open (&hd, mode->algo, 0);
+ if (err)
+ {
+ fprintf (stderr, PGM ": error opening hash `%s'\n",
+ gcry_md_algo_name (mode->algo));
+ exit (1);
+ }
+
+ obj->priv = hd;
+
+ return 0;
+}
+
+static void
+bench_hash_free (struct bench_obj *obj)
+{
+ gcry_md_hd_t hd = obj->priv;
+
+ gcry_md_close (hd);
+}
+
+static void
+bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+ gcry_md_hd_t hd = obj->priv;
+
+ gcry_md_write (hd, buf, buflen);
+ gcry_md_final (hd);
+}
+
+static struct bench_ops hash_ops = {
+ &bench_hash_init,
+ &bench_hash_free,
+ &bench_hash_do_bench
+};
+
+
+static struct bench_hash_mode hash_modes[] = {
+ {"", &hash_ops},
+ {0},
+};
+
+
+static void
+hash_bench_one (int algo, struct bench_hash_mode *pmode)
+{
+ struct bench_hash_mode mode = *pmode;
+ struct bench_obj obj = { 0 };
+ double result;
+
+ mode.algo = algo;
+
+ if (mode.name[0] == '\0')
+ printf (" %-14s | ", gcry_md_algo_name (algo));
+ else
+ printf (" %14s | ", mode.name);
+ fflush (stdout);
+
+ obj.ops = mode.ops;
+ obj.priv = &mode;
+
+ result = do_slope_benchmark (&obj);
+
+ bench_print_result (result);
+}
+
+static void
+__hash_bench (int algo)
+{
+ int i;
+
+ for (i = 0; hash_modes[i].name; i++)
+ hash_bench_one (algo, &hash_modes[i]);
+}
+
+void
+hash_bench (char **argv, int argc)
+{
+ int i, algo;
+
+ printf ("Hash:\n");
+
+ bench_print_header ("");
+
+ if (argv && argc)
+ {
+ for (i = 0; i < argc; i++)
+ {
+ algo = gcry_md_map_name (argv[i]);
+ if (algo)
+ __hash_bench (algo);
+ }
+ }
+ else
+ {
+ for (i = 1; i < 400; i++)
+ if (!gcry_md_test_algo (i))
+ __hash_bench (i);
+ }
+
+ bench_print_footer ();
+}
+
+
+/************************************************************** Main program. */
+
+void
+print_help (void)
+{
+ static const char *help_lines[] = {
+ "usage: bench-slope [options] [hash|cipher [algonames]]",
+ "",
+ " options:",
+ " --cpu-mhz <mhz> Set CPU speed for calculating cycles per bytes",
+ " results.",
+ " --disable-hwf <features> Disable hardware acceleration feature(s) for",
+ " benchmarking.",
+ NULL
+ };
+ const char **line;
+
+ for (line = help_lines; *line; line++)
+ fprintf (stdout, "%s\n", *line);
+}
+
+
+/* Warm up CPU. */
+static void
+warm_up_cpu (void)
+{
+ struct nsec_time start, end;
+
+ get_nsec_time (&start);
+ do
+ {
+ get_nsec_time (&end);
+ }
+ while (get_time_nsec_diff (&start, &end) < 1000.0 * 1000.0 * 1000.0);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ int debug = 0;
+
+ if (argc)
+ {
+ argc--;
+ argv++;
+ }
+
+ while (argc && last_argc != argc)
+ {
+ last_argc = argc;
+
+ if (!strcmp (*argv, "--"))
+ {
+ argc--;
+ argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--help"))
+ {
+ print_help ();
+ exit (0);
+ }
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose++;
+ argc--;
+ argv++;
+ }
+ else if (!strcmp (*argv, "--debug"))
+ {
+ verbose += 2;
+ debug++;
+ argc--;
+ argv++;
+ }
+ else if (!strcmp (*argv, "--disable-hwf"))
+ {
+ argc--;
+ argv++;
+ if (argc)
+ {
+ if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
+ fprintf (stderr,
+ PGM
+ ": unknown hardware feature `%s' - option ignored\n",
+ *argv);
+ argc--;
+ argv++;
+ }
+ }
+ else if (!strcmp (*argv, "--cpu-mhz"))
+ {
+ argc--;
+ argv++;
+ if (argc)
+ {
+ cpu_ghz = atof (*argv);
+ cpu_ghz /= 1000; /* Mhz => Ghz */
+
+ argc--;
+ argv++;
+ }
+ }
+ }
+
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
+
+ if (!gcry_check_version (GCRYPT_VERSION))
+ {
+ fprintf (stderr, PGM ": version mismatch; pgm=%s, library=%s\n",
+ GCRYPT_VERSION, gcry_check_version (NULL));
+ exit (1);
+ }
+
+ if (debug)
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+
+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+
+ if (!argc)
+ {
+ warm_up_cpu ();
+ hash_bench (NULL, 0);
+ cipher_bench (NULL, 0);
+ }
+ else if (!strcmp (*argv, "hash"))
+ {
+ argc--;
+ argv++;
+
+ warm_up_cpu ();
+ hash_bench ((argc == 0) ? NULL : argv, argc);
+ }
+ else if (!strcmp (*argv, "cipher"))
+ {
+ argc--;
+ argv++;
+
+ warm_up_cpu ();
+ cipher_bench ((argc == 0) ? NULL : argv, argc);
+ }
+ else
+ {
+ fprintf (stderr, "unknown argument: %s\n", *argv);
+ print_help ();
+ }
+
+ return 0;
+}
+
+#endif /* !NO_GET_NSEC_TIME */
More information about the Gcrypt-devel
mailing list