Tweak bench-slope parameters
[libgcrypt.git] / tests / bench-slope.c
1 /* bench-slope.c - for libgcrypt
2  * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi>
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser general Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <assert.h>
27 #include <time.h>
28
29 #ifdef _GCRYPT_IN_LIBGCRYPT
30 #include "../src/gcrypt-int.h"
31 #include "../compat/libcompat.h"
32 #else
33 #include <gcrypt.h>
34 #endif
35
36 #define PGM "bench-slope"
37
38 static int verbose;
39
40
41 /* CPU Ghz value provided by user, allows constructing cycles/byte and other
42    results.  */
43 static double cpu_ghz = -1;
44
45
46
47 /*************************************** Default parameters for measurements. */
48
49 /* Start at small buffer size, to get reasonable timer calibration for fast
50  * implementations (AES-NI etc). Sixteen selected to support the largest block
51  * size of current set cipher blocks. */
52 #define BUF_START_SIZE                  16
53
54 /* From ~0 to ~4kbytes give comparable results with results from academia
55  * (SUPERCOP). */
56 #define BUF_END_SIZE                    (BUF_START_SIZE + 4096)
57
58 /* With 128 byte steps, we get (4096)/64 = 64 data points. */
59 #define BUF_STEP_SIZE                   64
60
61 /* Number of repeated measurements at each data point. The median of these
62  * measurements is selected as data point further analysis. */
63 #define NUM_MEASUREMENT_REPETITIONS     64
64
65 /**************************************************** High-resolution timers. */
66
67 /* This benchmarking module needs needs high resolution timer.  */
68 #undef NO_GET_NSEC_TIME
69 #if defined(_WIN32)
70 struct nsec_time
71 {
72   LARGE_INTEGER perf_count;
73 };
74
75 static void
76 get_nsec_time (struct nsec_time *t)
77 {
78   BOOL ok;
79
80   ok = QueryPerformanceCounter (&t->perf_count);
81   assert (ok);
82 }
83
84 static double
85 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
86 {
87   static double nsecs_per_count = 0.0;
88   double nsecs;
89
90   if (nsecs_per_count == 0.0)
91     {
92       LARGE_INTEGER perf_freq;
93       BOOL ok;
94
95       /* Get counts per second. */
96       ok = QueryPerformanceFrequency (&perf_freq);
97       assert (ok);
98
99       nsecs_per_count = 1.0 / perf_freq.QuadPart;
100       nsecs_per_count *= 1000000.0 * 1000.0;    /* sec => nsec */
101
102       assert (nsecs_per_count > 0.0);
103     }
104
105   nsecs = end->perf_count.QuadPart - start->perf_count.QuadPart;        /* counts */
106   nsecs *= nsecs_per_count;     /* counts * (nsecs / count) => nsecs */
107
108   return nsecs;
109 }
110 #elif defined(HAVE_CLOCK_GETTIME)
111 struct nsec_time
112 {
113   struct timespec ts;
114 };
115
116 static void
117 get_nsec_time (struct nsec_time *t)
118 {
119   int err;
120
121   err = clock_gettime (CLOCK_REALTIME, &t->ts);
122   assert (err == 0);
123 }
124
125 static double
126 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
127 {
128   double nsecs;
129
130   nsecs = end->ts.tv_sec - start->ts.tv_sec;
131   nsecs *= 1000000.0 * 1000.0;  /* sec => nsec */
132
133   /* This way we don't have to care if tv_nsec unsigned or signed. */
134   if (end->ts.tv_nsec >= start->ts.tv_nsec)
135     nsecs += end->ts.tv_nsec - start->ts.tv_nsec;
136   else
137     nsecs -= start->ts.tv_nsec - end->ts.tv_nsec;
138
139   return nsecs;
140 }
141 #elif defined(HAVE_GETTIMEOFDAY)
142 struct nsec_time
143 {
144   struct timeval tv;
145 };
146
147 static void
148 get_nsec_time (struct nsec_time *t)
149 {
150   int err;
151
152   err = gettimeofday (&t->tv, NULL);
153   assert (err == 0);
154 }
155
156 static double
157 get_time_nsec_diff (struct nsec_time *start, struct nsec_time *end)
158 {
159   double nsecs;
160
161   nsecs = end->tv.tv_sec - start->tv.tv_sec;
162   nsecs *= 1000000;             /* sec => µsec */
163
164   /* This way we don't have to care if tv_usec unsigned or signed. */
165   if (end->tv.tv_usec >= start->tv.tv_usec)
166     nsecs += end->tv.tv_usec - start->tv.tv_usec;
167   else
168     nsecs -= start->tv.tv_usec - end->tv.tv_usec;
169
170   nsecs *= 1000;                /* µsec => nsec */
171
172   return nsecs;
173 }
174 #else
175 #define NO_GET_NSEC_TIME 1
176 #endif
177
178
179 /* If no high resolution timer found, provide dummy bench-slope.  */
180 #ifdef NO_GET_NSEC_TIME
181
182
183 int
184 main (void)
185 {
186   /* No nsec timer => SKIP test. */
187   return 77;
188 }
189
190
191 #else /* !NO_GET_NSEC_TIME */
192
193
194 /********************************************** Slope benchmarking framework. */
195
196 struct bench_obj
197 {
198   const struct bench_ops *ops;
199
200   unsigned int num_measure_repetitions;
201   unsigned int min_bufsize;
202   unsigned int max_bufsize;
203   unsigned int step_size;
204
205   void *priv;
206 };
207
208 typedef int (*const bench_initialize_t) (struct bench_obj * obj);
209 typedef void (*const bench_finalize_t) (struct bench_obj * obj);
210 typedef void (*const bench_do_run_t) (struct bench_obj * obj, void *buffer,
211                                       size_t buflen);
212
213 struct bench_ops
214 {
215   bench_initialize_t initialize;
216   bench_finalize_t finalize;
217   bench_do_run_t do_run;
218 };
219
220
221 double
222 get_slope (double (*const get_x) (unsigned int idx, void *priv),
223            void *get_x_priv, double y_points[], unsigned int npoints,
224            double *overhead)
225 {
226   double sumx, sumy, sumx2, sumy2, sumxy;
227   unsigned int i;
228   double b, a;
229
230   sumx = sumy = sumx2 = sumy2 = sumxy = 0;
231
232   for (i = 0; i < npoints; i++)
233     {
234       double x, y;
235
236       x = get_x (i, get_x_priv);        /* bytes */
237       y = y_points[i];          /* nsecs */
238
239       sumx += x;
240       sumy += y;
241       sumx2 += x * x;
242       /*sumy2 += y * y;*/
243       sumxy += x * y;
244     }
245
246   b = (npoints * sumxy - sumx * sumy) / (npoints * sumx2 - sumx * sumx);
247   a = (sumy - b * sumx) / npoints;
248
249   if (overhead)
250     *overhead = a;              /* nsecs */
251
252   return b;                     /* nsecs per byte */
253 }
254
255
256 double
257 get_bench_obj_point_x (unsigned int idx, void *priv)
258 {
259   struct bench_obj *obj = priv;
260   return (double) (obj->min_bufsize + (idx * obj->step_size));
261 }
262
263
264 unsigned int
265 get_num_measurements (struct bench_obj *obj)
266 {
267   unsigned int buf_range = obj->max_bufsize - obj->min_bufsize;
268   unsigned int num = buf_range / obj->step_size + 1;
269
270   while (obj->min_bufsize + (num * obj->step_size) > obj->max_bufsize)
271     num--;
272
273   return num + 1;
274 }
275
276
277 static int
278 double_cmp (const void *_a, const void *_b)
279 {
280   const double *a, *b;
281
282   a = _a;
283   b = _b;
284
285   if (*a > *b)
286     return 1;
287   if (*a < *b)
288     return -1;
289   return 0;
290 }
291
292
293 double
294 do_bench_obj_measurement (struct bench_obj *obj, void *buffer, size_t buflen,
295                           double *measurement_raw,
296                           unsigned int loop_iterations)
297 {
298   const unsigned int num_repetitions = obj->num_measure_repetitions;
299   const bench_do_run_t do_run = obj->ops->do_run;
300   struct nsec_time start, end;
301   unsigned int rep, loop;
302   double res;
303
304   if (num_repetitions < 1 || loop_iterations < 1)
305     return 0.0;
306
307   for (rep = 0; rep < num_repetitions; rep++)
308     {
309       get_nsec_time (&start);
310
311       for (loop = 0; loop < loop_iterations; loop++)
312         do_run (obj, buffer, buflen);
313
314       get_nsec_time (&end);
315
316       measurement_raw[rep] = get_time_nsec_diff (&start, &end);
317     }
318
319   /* Return median of repeated measurements. */
320   qsort (measurement_raw, num_repetitions, sizeof (measurement_raw[0]),
321          double_cmp);
322
323   if (num_repetitions % 2 == 1)
324     return measurement_raw[num_repetitions / 2];
325
326   res = measurement_raw[num_repetitions / 2]
327     + measurement_raw[num_repetitions / 2 - 1];
328   return res / 2;
329 }
330
331
332 unsigned int
333 adjust_loop_iterations_to_timer_accuracy (struct bench_obj *obj, void *buffer,
334                                           double *measurement_raw)
335 {
336   const double increase_thres = 3.0;
337   double tmp, nsecs;
338   unsigned int loop_iterations;
339   unsigned int test_bufsize;
340
341   test_bufsize = obj->min_bufsize;
342   if (test_bufsize == 0)
343     test_bufsize += obj->step_size;
344
345   loop_iterations = 0;
346   do
347     {
348       /* Increase loop iterations until we get other results than zero.  */
349       nsecs =
350         do_bench_obj_measurement (obj, buffer, test_bufsize,
351                                   measurement_raw, ++loop_iterations);
352     }
353   while (nsecs < 1.0 - 0.1);
354   do
355     {
356       /* Increase loop iterations until we get reasonable increase for elapsed time.  */
357       tmp =
358         do_bench_obj_measurement (obj, buffer, test_bufsize,
359                                   measurement_raw, ++loop_iterations);
360     }
361   while (tmp < nsecs * (increase_thres - 0.1));
362
363   return loop_iterations;
364 }
365
366
367 /* Benchmark and return linear regression slope in nanoseconds per byte.  */
368 double
369 do_slope_benchmark (struct bench_obj *obj)
370 {
371   unsigned int num_measurements;
372   double *measurements = NULL;
373   double *measurement_raw = NULL;
374   double slope, overhead;
375   unsigned int loop_iterations, midx, i;
376   unsigned char *real_buffer = NULL;
377   unsigned char *buffer;
378   size_t cur_bufsize;
379   int err;
380
381   err = obj->ops->initialize (obj);
382   if (err < 0)
383     return -1;
384
385   num_measurements = get_num_measurements (obj);
386   measurements = calloc (num_measurements, sizeof (*measurements));
387   if (!measurements)
388     goto err_free;
389
390   measurement_raw =
391     calloc (obj->num_measure_repetitions, sizeof (*measurement_raw));
392   if (!measurement_raw)
393     goto err_free;
394
395   if (num_measurements < 1 || obj->num_measure_repetitions < 1 ||
396       obj->max_bufsize < 1 || obj->min_bufsize > obj->max_bufsize)
397     goto err_free;
398
399   real_buffer = malloc (obj->max_bufsize + 128);
400   if (!real_buffer)
401     goto err_free;
402   /* Get aligned buffer */
403   buffer = real_buffer;
404   buffer += 128 - ((real_buffer - (unsigned char *) 0) & (128 - 1));
405
406   for (i = 0; i < obj->max_bufsize; i++)
407     buffer[i] = 0x55 ^ (-i);
408
409   /* Adjust number of loop iterations up to timer accuracy.  */
410   loop_iterations = adjust_loop_iterations_to_timer_accuracy (obj, buffer,
411                                                               measurement_raw);
412
413   /* Perform measurements */
414   for (midx = 0, cur_bufsize = obj->min_bufsize;
415        cur_bufsize <= obj->max_bufsize; cur_bufsize += obj->step_size, midx++)
416     {
417       measurements[midx] =
418         do_bench_obj_measurement (obj, buffer, cur_bufsize, measurement_raw,
419                                   loop_iterations);
420       measurements[midx] /= loop_iterations;
421     }
422
423   assert (midx == num_measurements);
424
425   slope =
426     get_slope (&get_bench_obj_point_x, obj, measurements, num_measurements,
427                &overhead);
428
429   free (measurement_raw);
430   free (real_buffer);
431   obj->ops->finalize (obj);
432
433   return slope;
434
435 err_free:
436   if (measurement_raw)
437     free (measurement_raw);
438   if (measurements)
439     free (measurements);
440   if (real_buffer)
441     free (real_buffer);
442   obj->ops->finalize (obj);
443
444   return -1;
445 }
446
447
448 /********************************************************** Printing results. */
449
450 static void
451 double_to_str (char *out, size_t outlen, double value)
452 {
453   const char *fmt;
454
455   if (value < 1.0)
456     fmt = "%.3f";
457   else if (value < 100.0)
458     fmt = "%.2f";
459   else
460     fmt = "%.1f";
461
462   snprintf (out, outlen, fmt, value);
463 }
464
465 static void
466 bench_print_result (double nsecs_per_byte)
467 {
468   double cycles_per_byte, mbytes_per_sec;
469   char nsecpbyte_buf[16];
470   char mbpsec_buf[16];
471   char cpbyte_buf[16];
472
473   strcpy (cpbyte_buf, "-");
474
475   double_to_str (nsecpbyte_buf, sizeof (nsecpbyte_buf), nsecs_per_byte);
476
477   /* If user didn't provide CPU speed, we cannot show cycles/byte results.  */
478   if (cpu_ghz > 0.0)
479     {
480       cycles_per_byte = nsecs_per_byte * cpu_ghz;
481       double_to_str (cpbyte_buf, sizeof (cpbyte_buf), cycles_per_byte);
482     }
483
484   mbytes_per_sec =
485     (1000.0 * 1000.0 * 1000.0) / (nsecs_per_byte * 1024 * 1024);
486   double_to_str (mbpsec_buf, sizeof (mbpsec_buf), mbytes_per_sec);
487
488   strncat (nsecpbyte_buf, " ns/B", sizeof (nsecpbyte_buf) - 1);
489   strncat (mbpsec_buf, " MiB/s", sizeof (mbpsec_buf) - 1);
490   strncat (cpbyte_buf, " c/B", sizeof (cpbyte_buf) - 1);
491
492   printf ("%14s %15s %13s\n", nsecpbyte_buf, mbpsec_buf, cpbyte_buf);
493 }
494
495 static void
496 bench_print_header (const char *algo_name)
497 {
498   printf (" %-14s | ", algo_name);
499   printf ("%14s %15s %13s\n", "nanosecs/byte", "mebibytes/sec",
500           "cycles/byte");
501 }
502
503 static void
504 bench_print_footer (void)
505 {
506   printf (" %-14s =\n", "");
507 }
508
509
510 /********************************************************* Cipher benchmarks. */
511
512 struct bench_cipher_mode
513 {
514   int mode;
515   const char *name;
516   struct bench_ops *ops;
517
518   int algo;
519 };
520
521
522 static int
523 bench_encrypt_init (struct bench_obj *obj)
524 {
525   struct bench_cipher_mode *mode = obj->priv;
526   gcry_cipher_hd_t hd;
527   int err, keylen;
528
529   obj->min_bufsize = BUF_START_SIZE;
530   obj->max_bufsize = BUF_END_SIZE;
531   obj->step_size = BUF_STEP_SIZE;
532   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
533
534   err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0);
535   if (err)
536     {
537       fprintf (stderr, PGM ": error opening cipher `%s'\n",
538                gcry_cipher_algo_name (mode->algo));
539       exit (1);
540     }
541
542   keylen = gcry_cipher_get_algo_keylen (mode->algo);
543   if (keylen)
544     {
545       char key[keylen];
546       int i;
547
548       for (i = 0; i < keylen; i++)
549         key[i] = 0x33 ^ (11 - i);
550
551       err = gcry_cipher_setkey (hd, key, keylen);
552       if (err)
553         {
554           fprintf (stderr, PGM ": gcry_cipher_setkey failed: %s\n",
555                    gpg_strerror (err));
556           gcry_cipher_close (hd);
557           exit (1);
558         }
559     }
560   else
561     {
562       fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n",
563                gcry_cipher_algo_name (mode->algo));
564       gcry_cipher_close (hd);
565       exit (1);
566     }
567
568   obj->priv = hd;
569
570   return 0;
571 }
572
573 static void
574 bench_encrypt_free (struct bench_obj *obj)
575 {
576   gcry_cipher_hd_t hd = obj->priv;
577
578   gcry_cipher_close (hd);
579 }
580
581 static void
582 bench_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
583 {
584   gcry_cipher_hd_t hd = obj->priv;
585   int err;
586
587   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
588   if (err)
589     {
590       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
591                gpg_strerror (err));
592       gcry_cipher_close (hd);
593       exit (1);
594     }
595 }
596
597 static void
598 bench_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
599 {
600   gcry_cipher_hd_t hd = obj->priv;
601   int err;
602
603   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
604   if (err)
605     {
606       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
607                gpg_strerror (err));
608       gcry_cipher_close (hd);
609       exit (1);
610     }
611 }
612
613 static struct bench_ops encrypt_ops = {
614   &bench_encrypt_init,
615   &bench_encrypt_free,
616   &bench_encrypt_do_bench
617 };
618
619 static struct bench_ops decrypt_ops = {
620   &bench_encrypt_init,
621   &bench_encrypt_free,
622   &bench_decrypt_do_bench
623 };
624
625
626
627 static void
628 bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
629 {
630   gcry_cipher_hd_t hd = obj->priv;
631   int err;
632   char tag[8];
633   char nonce[11] = { 0x80, 0x01, };
634   size_t params[3];
635
636   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
637
638   /* Set CCM lengths */
639   params[0] = buflen;
640   params[1] = 0;                /*aadlen */
641   params[2] = sizeof (tag);
642   err =
643     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
644   if (err)
645     {
646       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
647                gpg_strerror (err));
648       gcry_cipher_close (hd);
649       exit (1);
650     }
651
652   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
653   if (err)
654     {
655       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
656                gpg_strerror (err));
657       gcry_cipher_close (hd);
658       exit (1);
659     }
660
661   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
662   if (err)
663     {
664       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
665                gpg_strerror (err));
666       gcry_cipher_close (hd);
667       exit (1);
668     }
669 }
670
671 static void
672 bench_ccm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
673 {
674   gcry_cipher_hd_t hd = obj->priv;
675   int err;
676   char tag[8] = { 0, };
677   char nonce[11] = { 0x80, 0x01, };
678   size_t params[3];
679
680   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
681
682   /* Set CCM lengths */
683   params[0] = buflen;
684   params[1] = 0;                /*aadlen */
685   params[2] = sizeof (tag);
686   err =
687     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
688   if (err)
689     {
690       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
691                gpg_strerror (err));
692       gcry_cipher_close (hd);
693       exit (1);
694     }
695
696   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
697   if (err)
698     {
699       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
700                gpg_strerror (err));
701       gcry_cipher_close (hd);
702       exit (1);
703     }
704
705   err = gcry_cipher_checktag (hd, tag, sizeof (tag));
706   if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
707     err = gpg_error (GPG_ERR_NO_ERROR);
708   if (err)
709     {
710       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
711                gpg_strerror (err));
712       gcry_cipher_close (hd);
713       exit (1);
714     }
715 }
716
717 static void
718 bench_ccm_authenticate_do_bench (struct bench_obj *obj, void *buf,
719                                  size_t buflen)
720 {
721   gcry_cipher_hd_t hd = obj->priv;
722   int err;
723   char tag[8] = { 0, };
724   char nonce[11] = { 0x80, 0x01, };
725   size_t params[3];
726   char data = 0xff;
727
728   gcry_cipher_setiv (hd, nonce, sizeof (nonce));
729
730   /* Set CCM lengths */
731   params[0] = sizeof (data);    /*datalen */
732   params[1] = buflen;           /*aadlen */
733   params[2] = sizeof (tag);
734   err =
735     gcry_cipher_ctl (hd, GCRYCTL_SET_CCM_LENGTHS, params, sizeof (params));
736   if (err)
737     {
738       fprintf (stderr, PGM ": gcry_cipher_ctl failed: %s\n",
739                gpg_strerror (err));
740       gcry_cipher_close (hd);
741       exit (1);
742     }
743
744   err = gcry_cipher_authenticate (hd, buf, buflen);
745   if (err)
746     {
747       fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n",
748                gpg_strerror (err));
749       gcry_cipher_close (hd);
750       exit (1);
751     }
752
753   err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
754   if (err)
755     {
756       fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
757                gpg_strerror (err));
758       gcry_cipher_close (hd);
759       exit (1);
760     }
761
762   err = gcry_cipher_gettag (hd, tag, sizeof (tag));
763   if (err)
764     {
765       fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
766                gpg_strerror (err));
767       gcry_cipher_close (hd);
768       exit (1);
769     }
770 }
771
772 static struct bench_ops ccm_encrypt_ops = {
773   &bench_encrypt_init,
774   &bench_encrypt_free,
775   &bench_ccm_encrypt_do_bench
776 };
777
778 static struct bench_ops ccm_decrypt_ops = {
779   &bench_encrypt_init,
780   &bench_encrypt_free,
781   &bench_ccm_decrypt_do_bench
782 };
783
784 static struct bench_ops ccm_authenticate_ops = {
785   &bench_encrypt_init,
786   &bench_encrypt_free,
787   &bench_ccm_authenticate_do_bench
788 };
789
790
791 static struct bench_cipher_mode cipher_modes[] = {
792   {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
793   {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops},
794   {GCRY_CIPHER_MODE_CBC, "CBC enc", &encrypt_ops},
795   {GCRY_CIPHER_MODE_CBC, "CBC dec", &decrypt_ops},
796   {GCRY_CIPHER_MODE_CFB, "CFB enc", &encrypt_ops},
797   {GCRY_CIPHER_MODE_CFB, "CFB dec", &decrypt_ops},
798   {GCRY_CIPHER_MODE_OFB, "OFB enc", &encrypt_ops},
799   {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops},
800   {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops},
801   {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops},
802   {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
803   {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
804   {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
805   {0},
806 };
807
808
809 static void
810 cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
811 {
812   struct bench_cipher_mode mode = *pmode;
813   struct bench_obj obj = { 0 };
814   double result;
815   unsigned int blklen;
816
817   mode.algo = algo;
818
819   /* Check if this mode is ok */
820   blklen = gcry_cipher_get_algo_blklen (algo);
821   if (!blklen)
822     return;
823
824   /* Stream cipher? Only test with ECB. */
825   if (blklen == 1 && mode.mode != GCRY_CIPHER_MODE_ECB)
826     return;
827   if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB)
828     {
829       mode.mode = GCRY_CIPHER_MODE_STREAM;
830       mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec";
831     }
832
833   /* CCM has restrictions for block-size */
834   if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
835     return;
836
837   printf (" %14s | ", mode.name);
838   fflush (stdout);
839
840   obj.ops = mode.ops;
841   obj.priv = &mode;
842
843   result = do_slope_benchmark (&obj);
844
845   bench_print_result (result);
846 }
847
848
849 static void
850 _cipher_bench (int algo)
851 {
852   const char *algoname;
853   int i;
854
855   algoname = gcry_cipher_algo_name (algo);
856
857   bench_print_header (algoname);
858
859   for (i = 0; cipher_modes[i].mode; i++)
860     cipher_bench_one (algo, &cipher_modes[i]);
861
862   bench_print_footer ();
863 }
864
865
866 void
867 cipher_bench (char **argv, int argc)
868 {
869   int i, algo;
870
871   printf ("Cipher:\n");
872
873   if (argv && argc)
874     {
875       for (i = 0; i < argc; i++)
876         {
877           algo = gcry_cipher_map_name (argv[i]);
878           if (algo)
879             _cipher_bench (algo);
880         }
881     }
882   else
883     {
884       for (i = 1; i < 400; i++)
885         if (!gcry_cipher_test_algo (i))
886           _cipher_bench (i);
887     }
888 }
889
890
891 /*********************************************************** Hash benchmarks. */
892
893 struct bench_hash_mode
894 {
895   const char *name;
896   struct bench_ops *ops;
897
898   int algo;
899 };
900
901
902 static int
903 bench_hash_init (struct bench_obj *obj)
904 {
905   struct bench_hash_mode *mode = obj->priv;
906   gcry_md_hd_t hd;
907   int err;
908
909   obj->min_bufsize = BUF_START_SIZE;
910   obj->max_bufsize = BUF_END_SIZE;
911   obj->step_size = BUF_STEP_SIZE;
912   obj->num_measure_repetitions = NUM_MEASUREMENT_REPETITIONS;
913
914   err = gcry_md_open (&hd, mode->algo, 0);
915   if (err)
916     {
917       fprintf (stderr, PGM ": error opening hash `%s'\n",
918                gcry_md_algo_name (mode->algo));
919       exit (1);
920     }
921
922   obj->priv = hd;
923
924   return 0;
925 }
926
927 static void
928 bench_hash_free (struct bench_obj *obj)
929 {
930   gcry_md_hd_t hd = obj->priv;
931
932   gcry_md_close (hd);
933 }
934
935 static void
936 bench_hash_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
937 {
938   gcry_md_hd_t hd = obj->priv;
939
940   gcry_md_write (hd, buf, buflen);
941   gcry_md_final (hd);
942 }
943
944 static struct bench_ops hash_ops = {
945   &bench_hash_init,
946   &bench_hash_free,
947   &bench_hash_do_bench
948 };
949
950
951 static struct bench_hash_mode hash_modes[] = {
952   {"", &hash_ops},
953   {0},
954 };
955
956
957 static void
958 hash_bench_one (int algo, struct bench_hash_mode *pmode)
959 {
960   struct bench_hash_mode mode = *pmode;
961   struct bench_obj obj = { 0 };
962   double result;
963
964   mode.algo = algo;
965
966   if (mode.name[0] == '\0')
967     printf (" %-14s | ", gcry_md_algo_name (algo));
968   else
969     printf (" %14s | ", mode.name);
970   fflush (stdout);
971
972   obj.ops = mode.ops;
973   obj.priv = &mode;
974
975   result = do_slope_benchmark (&obj);
976
977   bench_print_result (result);
978 }
979
980 static void
981 _hash_bench (int algo)
982 {
983   int i;
984
985   for (i = 0; hash_modes[i].name; i++)
986     hash_bench_one (algo, &hash_modes[i]);
987 }
988
989 void
990 hash_bench (char **argv, int argc)
991 {
992   int i, algo;
993
994   printf ("Hash:\n");
995
996   bench_print_header ("");
997
998   if (argv && argc)
999     {
1000       for (i = 0; i < argc; i++)
1001         {
1002           algo = gcry_md_map_name (argv[i]);
1003           if (algo)
1004             _hash_bench (algo);
1005         }
1006     }
1007   else
1008     {
1009       for (i = 1; i < 400; i++)
1010         if (!gcry_md_test_algo (i))
1011           _hash_bench (i);
1012     }
1013
1014   bench_print_footer ();
1015 }
1016
1017
1018 /************************************************************** Main program. */
1019
1020 void
1021 print_help (void)
1022 {
1023   static const char *help_lines[] = {
1024     "usage: bench-slope [options] [hash|cipher [algonames]]",
1025     "",
1026     " options:",
1027     "     --cpu-mhz <mhz>           Set CPU speed for calculating cycles per bytes",
1028     "                               results.",
1029     "     --disable-hwf <features>  Disable hardware acceleration feature(s) for",
1030     "                               benchmarking.",
1031     NULL
1032   };
1033   const char **line;
1034
1035   for (line = help_lines; *line; line++)
1036     fprintf (stdout, "%s\n", *line);
1037 }
1038
1039
1040 /* Warm up CPU.  */
1041 static void
1042 warm_up_cpu (void)
1043 {
1044   struct nsec_time start, end;
1045
1046   get_nsec_time (&start);
1047   do
1048     {
1049       get_nsec_time (&end);
1050     }
1051   while (get_time_nsec_diff (&start, &end) < 1000.0 * 1000.0 * 1000.0);
1052 }
1053
1054
1055 int
1056 main (int argc, char **argv)
1057 {
1058   int last_argc = -1;
1059   int debug = 0;
1060
1061   if (argc)
1062     {
1063       argc--;
1064       argv++;
1065     }
1066
1067   /* We skip this test if we are running under the test suite (no args
1068      and srcdir defined) and GCRYPT_NO_BENCHMARKS is set.  */
1069   if (!argc && getenv ("srcdir") && getenv ("GCRYPT_NO_BENCHMARKS"))
1070     exit (77);
1071
1072   while (argc && last_argc != argc)
1073     {
1074       last_argc = argc;
1075
1076       if (!strcmp (*argv, "--"))
1077         {
1078           argc--;
1079           argv++;
1080           break;
1081         }
1082       else if (!strcmp (*argv, "--help"))
1083         {
1084           print_help ();
1085           exit (0);
1086         }
1087       else if (!strcmp (*argv, "--verbose"))
1088         {
1089           verbose++;
1090           argc--;
1091           argv++;
1092         }
1093       else if (!strcmp (*argv, "--debug"))
1094         {
1095           verbose += 2;
1096           debug++;
1097           argc--;
1098           argv++;
1099         }
1100       else if (!strcmp (*argv, "--disable-hwf"))
1101         {
1102           argc--;
1103           argv++;
1104           if (argc)
1105             {
1106               if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
1107                 fprintf (stderr,
1108                          PGM
1109                          ": unknown hardware feature `%s' - option ignored\n",
1110                          *argv);
1111               argc--;
1112               argv++;
1113             }
1114         }
1115       else if (!strcmp (*argv, "--cpu-mhz"))
1116         {
1117           argc--;
1118           argv++;
1119           if (argc)
1120             {
1121               cpu_ghz = atof (*argv);
1122               cpu_ghz /= 1000;  /* Mhz => Ghz */
1123
1124               argc--;
1125               argv++;
1126             }
1127         }
1128     }
1129
1130   gcry_control (GCRYCTL_SET_VERBOSITY, (int) verbose);
1131
1132   if (!gcry_check_version (GCRYPT_VERSION))
1133     {
1134       fprintf (stderr, PGM ": version mismatch; pgm=%s, library=%s\n",
1135                GCRYPT_VERSION, gcry_check_version (NULL));
1136       exit (1);
1137     }
1138
1139   if (debug)
1140     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
1141
1142   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
1143   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
1144   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
1145
1146   if (!argc)
1147     {
1148       warm_up_cpu ();
1149       hash_bench (NULL, 0);
1150       cipher_bench (NULL, 0);
1151     }
1152   else if (!strcmp (*argv, "hash"))
1153     {
1154       argc--;
1155       argv++;
1156
1157       warm_up_cpu ();
1158       hash_bench ((argc == 0) ? NULL : argv, argc);
1159     }
1160   else if (!strcmp (*argv, "cipher"))
1161     {
1162       argc--;
1163       argv++;
1164
1165       warm_up_cpu ();
1166       cipher_bench ((argc == 0) ? NULL : argv, argc);
1167     }
1168   else
1169     {
1170       fprintf (stderr, PGM ": unknown argument: %s\n", *argv);
1171       print_help ();
1172     }
1173
1174   return 0;
1175 }
1176
1177 #endif /* !NO_GET_NSEC_TIME */