Revert "Merge branch 'upstream' into tizen"
[platform/upstream/nettle.git] / examples / nettle-benchmark.c
1 /* nettle-benchmark.c
2  *
3  * Tries the performance of the various algorithms.
4  *
5  */
6  
7 /* nettle, low-level cryptographics library
8  *
9  * Copyright (C) 2001, 2010 Niels Möller
10  *  
11  * The nettle library is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or (at your
14  * option) any later version.
15  * 
16  * The nettle library is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
19  * License for more details.
20  * 
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with the nettle library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  * MA 02111-1301, USA.
25  */
26
27 #if HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <math.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <time.h>
40
41 #include "timing.h"
42
43 #include "aes.h"
44 #include "arcfour.h"
45 #include "blowfish.h"
46 #include "cast128.h"
47 #include "cbc.h"
48 #include "ctr.h"
49 #include "des.h"
50 #include "gcm.h"
51 #include "memxor.h"
52 #include "salsa20.h"
53 #include "serpent.h"
54 #include "sha1.h"
55 #include "sha2.h"
56 #include "sha3.h"
57 #include "twofish.h"
58 #include "umac.h"
59
60 #include "nettle-meta.h"
61 #include "nettle-internal.h"
62
63 #include "getopt.h"
64
65 static double frequency = 0.0;
66
67 /* Process BENCH_BLOCK bytes at a time, for BENCH_INTERVAL seconds. */
68 #define BENCH_BLOCK 10240
69 #define BENCH_INTERVAL 0.1
70
71 /* FIXME: Proper configure test for rdtsc? */
72 #ifndef WITH_CYCLE_COUNTER
73 # if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
74 #  define WITH_CYCLE_COUNTER 1
75 # else
76 #  define WITH_CYCLE_COUNTER 0
77 # endif
78 #endif
79
80 #if WITH_CYCLE_COUNTER
81 # if defined(__i386__)
82 #define GET_CYCLE_COUNTER(hi, lo)               \
83   __asm__("xorl %%eax,%%eax\n"                  \
84           "movl %%ebx, %%edi\n"                 \
85           "cpuid\n"                             \
86           "rdtsc\n"                             \
87           "movl %%edi, %%ebx\n"                 \
88           : "=a" (lo), "=d" (hi)                \
89           : /* No inputs. */                    \
90           : "%edi", "%ecx", "cc")
91 # elif defined(__x86_64__)
92 #define GET_CYCLE_COUNTER(hi, lo)               \
93   __asm__("xorl %%eax,%%eax\n"                  \
94           "mov %%rbx, %%r10\n"                  \
95           "cpuid\n"                             \
96           "rdtsc\n"                             \
97           "mov %%r10, %%rbx\n"                  \
98           : "=a" (lo), "=d" (hi)                \
99           : /* No inputs. */                    \
100           : "%r10", "%rcx", "cc")
101 # endif
102 #define BENCH_ITERATIONS 10
103 #endif
104
105 static void NORETURN PRINTF_STYLE(1,2)
106 die(const char *format, ...)
107 {
108   va_list args;
109   va_start(args, format);
110   vfprintf(stderr, format, args);
111   va_end(args);
112
113   exit(EXIT_FAILURE);
114 }
115
116 static double overhead = 0.0; 
117
118 /* Returns second per function call */
119 static double
120 time_function(void (*f)(void *arg), void *arg)
121 {
122   unsigned ncalls;
123   double elapsed;
124
125   for (ncalls = 10 ;;)
126     {
127       unsigned i;
128
129       time_start();
130       for (i = 0; i < ncalls; i++)
131         f(arg);
132       elapsed = time_end();
133       if (elapsed > BENCH_INTERVAL)
134         break;
135       else if (elapsed < BENCH_INTERVAL / 10)
136         ncalls *= 10;
137       else
138         ncalls *= 2;
139     }
140   return elapsed / ncalls - overhead;
141 }
142
143 static void
144 bench_nothing(void *arg UNUSED)
145 {
146   return;
147 }
148
149 struct bench_memxor_info
150 {
151   uint8_t *dst;
152   const uint8_t *src;
153   const uint8_t *other;  
154 };
155
156 static void
157 bench_memxor(void *arg)
158 {
159   struct bench_memxor_info *info = arg;
160   memxor (info->dst, info->src, BENCH_BLOCK);
161 }
162
163 static void
164 bench_memxor3(void *arg)
165 {
166   struct bench_memxor_info *info = arg;
167   memxor3 (info->dst, info->src, info->other, BENCH_BLOCK);
168 }
169
170 struct bench_hash_info
171 {
172   void *ctx;
173   nettle_hash_update_func *update;
174   const uint8_t *data;
175 };
176
177 static void
178 bench_hash(void *arg)
179 {
180   struct bench_hash_info *info = arg;
181   info->update(info->ctx, BENCH_BLOCK, info->data);
182 }
183
184 struct bench_cipher_info
185 {
186   void *ctx;
187   nettle_crypt_func *crypt;
188   uint8_t *data;
189 };
190
191 static void
192 bench_cipher(void *arg)
193 {
194   struct bench_cipher_info *info = arg;
195   info->crypt(info->ctx, BENCH_BLOCK, info->data, info->data);
196 }
197
198 struct bench_cbc_info
199 {
200   void *ctx;
201   nettle_crypt_func *crypt;
202  
203   uint8_t *data;
204   
205   unsigned block_size;
206   uint8_t *iv;
207 };
208
209 static void
210 bench_cbc_encrypt(void *arg)
211 {
212   struct bench_cbc_info *info = arg;
213   cbc_encrypt(info->ctx, info->crypt,
214               info->block_size, info->iv,
215               BENCH_BLOCK, info->data, info->data);
216 }
217
218 static void
219 bench_cbc_decrypt(void *arg)
220 {
221   struct bench_cbc_info *info = arg;
222   cbc_decrypt(info->ctx, info->crypt,
223               info->block_size, info->iv,
224               BENCH_BLOCK, info->data, info->data);
225 }
226
227 static void
228 bench_ctr(void *arg)
229 {
230   struct bench_cbc_info *info = arg;
231   ctr_crypt(info->ctx, info->crypt,
232             info->block_size, info->iv,
233             BENCH_BLOCK, info->data, info->data);
234 }
235
236 /* Set data[i] = floor(sqrt(i)) */
237 static void
238 init_data(uint8_t *data)
239 {
240   unsigned i,j;
241   for (i = j = 0; i<BENCH_BLOCK;  i++)
242     {
243       if (j*j < i)
244         j++;
245       data[i] = j;
246     }
247 }
248
249 static void
250 init_key(unsigned length,
251          uint8_t *key)
252 {
253   unsigned i;
254   for (i = 0; i<length; i++)
255     key[i] = i;
256 }
257
258 static void
259 header(void)
260 {
261   printf("%18s %11s Mbyte/s%s\n",
262          "Algorithm", "mode", 
263          frequency > 0.0 ? " cycles/byte cycles/block" : "");  
264 }
265
266 static void
267 display(const char *name, const char *mode, unsigned block_size,
268         double time)
269 {
270   printf("%18s %11s %7.2f",
271          name, mode,
272          BENCH_BLOCK / (time * 1048576.0));
273   if (frequency > 0.0)
274     {
275       printf(" %11.2f", time * frequency / BENCH_BLOCK);
276       if (block_size > 0)
277         printf(" %12.2f", time * frequency * block_size / BENCH_BLOCK);
278     }
279   printf("\n");
280 }
281
282 static void *
283 xalloc(size_t size)
284 {
285   void *p = malloc(size);
286   if (!p)
287     die("Virtual memory exhausted.\n");
288
289   return p;
290 }
291
292 static void
293 time_overhead(void)
294 {
295   overhead = time_function(bench_nothing, NULL);
296   printf("benchmark call overhead: %7f us", overhead * 1e6);
297   if (frequency > 0.0)
298     printf("%7.2f cycles\n", overhead * frequency);
299   printf("\n");  
300 }
301
302
303
304 static void
305 time_memxor(void)
306 {
307   struct bench_memxor_info info;
308   uint8_t src[BENCH_BLOCK + sizeof(long)];
309   uint8_t other[BENCH_BLOCK + sizeof(long)];
310   uint8_t dst[BENCH_BLOCK];
311
312   info.src = src;
313   info.dst = dst;
314
315   display ("memxor", "aligned", sizeof(unsigned long),
316            time_function(bench_memxor, &info));
317   info.src = src + 1;
318   display ("memxor", "unaligned", sizeof(unsigned long),
319            time_function(bench_memxor, &info));
320
321   info.src = src;
322   info.other = other;
323   display ("memxor3", "aligned", sizeof(unsigned long),
324            time_function(bench_memxor3, &info));
325
326   info.other = other + 1;
327   display ("memxor3", "unaligned01", sizeof(unsigned long),
328            time_function(bench_memxor3, &info));
329   info.src = src + 1;
330   display ("memxor3", "unaligned11", sizeof(unsigned long),
331            time_function(bench_memxor3, &info));
332   info.other = other + 2;
333   display ("memxor3", "unaligned12", sizeof(unsigned long),
334            time_function(bench_memxor3, &info));  
335 }
336
337 static void
338 time_hash(const struct nettle_hash *hash)
339 {
340   static uint8_t data[BENCH_BLOCK];
341   struct bench_hash_info info;
342
343   info.ctx = xalloc(hash->context_size); 
344   info.update = hash->update;
345   info.data = data;
346
347   init_data(data);
348   hash->init(info.ctx);
349
350   display(hash->name, "update", hash->block_size,
351           time_function(bench_hash, &info));
352
353   free(info.ctx);
354 }
355
356 static void
357 time_umac(void)
358 {
359   static uint8_t data[BENCH_BLOCK];
360   struct bench_hash_info info;
361   struct umac32_ctx ctx32;
362   struct umac64_ctx ctx64;
363   struct umac96_ctx ctx96;
364   struct umac128_ctx ctx128;
365   
366   uint8_t key[16];
367
368   umac32_set_key (&ctx32, key);
369   info.ctx = &ctx32;
370   info.update = (nettle_hash_update_func *) umac32_update;
371   info.data = data;
372
373   display("umac32", "update", UMAC_DATA_SIZE,
374           time_function(bench_hash, &info));
375
376   umac64_set_key (&ctx64, key);
377   info.ctx = &ctx64;
378   info.update = (nettle_hash_update_func *) umac64_update;
379   info.data = data;
380
381   display("umac64", "update", UMAC_DATA_SIZE,
382           time_function(bench_hash, &info));
383
384   umac96_set_key (&ctx96, key);
385   info.ctx = &ctx96;
386   info.update = (nettle_hash_update_func *) umac96_update;
387   info.data = data;
388
389   display("umac96", "update", UMAC_DATA_SIZE,
390           time_function(bench_hash, &info));
391
392   umac128_set_key (&ctx128, key);
393   info.ctx = &ctx128;
394   info.update = (nettle_hash_update_func *) umac128_update;
395   info.data = data;
396
397   display("umac128", "update", UMAC_DATA_SIZE,
398           time_function(bench_hash, &info));
399 }
400
401 static void
402 time_gcm(void)
403 {
404   static uint8_t data[BENCH_BLOCK];
405   struct bench_hash_info hinfo;
406   struct bench_cipher_info cinfo;
407   struct gcm_aes_ctx ctx;
408
409   uint8_t key[16];
410   uint8_t iv[GCM_IV_SIZE];
411
412   gcm_aes_set_key(&ctx, sizeof(key), key);
413   gcm_aes_set_iv(&ctx, sizeof(iv), iv);
414
415   hinfo.ctx = &ctx;
416   hinfo.update = (nettle_hash_update_func *) gcm_aes_update;
417   hinfo.data = data;
418   
419   display("gcm-aes", "update", GCM_BLOCK_SIZE,
420           time_function(bench_hash, &hinfo));
421   
422   cinfo.ctx = &ctx;
423   cinfo.crypt = (nettle_crypt_func *) gcm_aes_encrypt;
424   cinfo.data = data;
425
426   display("gcm-aes", "encrypt", GCM_BLOCK_SIZE,
427           time_function(bench_cipher, &cinfo));
428
429   cinfo.crypt = (nettle_crypt_func *) gcm_aes_decrypt;
430
431   display("gcm-aes", "decrypt", GCM_BLOCK_SIZE,
432           time_function(bench_cipher, &cinfo));
433 }
434
435 static int
436 prefix_p(const char *prefix, const char *s)
437 {
438   size_t i;
439   for (i = 0; prefix[i]; i++)
440     if (prefix[i] != s[i])
441       return 0;
442   return 1;
443 }
444
445 static int
446 block_cipher_p(const struct nettle_cipher *cipher)
447 {
448   /* Don't use nettle cbc and ctr for openssl ciphers. */
449   return cipher->block_size > 0 && !prefix_p("openssl", cipher->name);
450 }
451
452 static void
453 time_cipher(const struct nettle_cipher *cipher)
454 {
455   void *ctx = xalloc(cipher->context_size);
456   uint8_t *key = xalloc(cipher->key_size);
457
458   static uint8_t data[BENCH_BLOCK];
459
460   printf("\n");
461   
462   init_data(data);
463
464   {
465     /* Decent initializers are a GNU extension, so don't use it here. */
466     struct bench_cipher_info info;
467     info.ctx = ctx;
468     info.crypt = cipher->encrypt;
469     info.data = data;
470     
471     init_key(cipher->key_size, key);
472     cipher->set_encrypt_key(ctx, cipher->key_size, key);
473
474     display(cipher->name, "ECB encrypt", cipher->block_size,
475             time_function(bench_cipher, &info));
476   }
477   
478   {
479     struct bench_cipher_info info;
480     info.ctx = ctx;
481     info.crypt = cipher->decrypt;
482     info.data = data;
483     
484     init_key(cipher->key_size, key);
485     cipher->set_decrypt_key(ctx, cipher->key_size, key);
486
487     display(cipher->name, "ECB decrypt", cipher->block_size,
488             time_function(bench_cipher, &info));
489   }
490
491   if (block_cipher_p(cipher))
492     {
493       uint8_t *iv = xalloc(cipher->block_size);
494       
495       /* Do CBC mode */
496       {
497         struct bench_cbc_info info;
498         info.ctx = ctx;
499         info.crypt = cipher->encrypt;
500         info.data = data;
501         info.block_size = cipher->block_size;
502         info.iv = iv;
503     
504         memset(iv, 0, sizeof(iv));
505     
506         cipher->set_encrypt_key(ctx, cipher->key_size, key);
507
508         display(cipher->name, "CBC encrypt", cipher->block_size,
509                 time_function(bench_cbc_encrypt, &info));
510       }
511
512       {
513         struct bench_cbc_info info;
514         info.ctx = ctx;
515         info.crypt = cipher->decrypt;
516         info.data = data;
517         info.block_size = cipher->block_size;
518         info.iv = iv;
519     
520         memset(iv, 0, sizeof(iv));
521
522         cipher->set_decrypt_key(ctx, cipher->key_size, key);
523
524         display(cipher->name, "CBC decrypt", cipher->block_size,
525                 time_function(bench_cbc_decrypt, &info));
526       }
527
528       /* Do CTR mode */
529       {
530         struct bench_cbc_info info;
531         info.ctx = ctx;
532         info.crypt = cipher->encrypt;
533         info.data = data;
534         info.block_size = cipher->block_size;
535         info.iv = iv;
536     
537         memset(iv, 0, sizeof(iv));
538     
539         cipher->set_encrypt_key(ctx, cipher->key_size, key);
540
541         display(cipher->name, "CTR", cipher->block_size,
542                 time_function(bench_ctr, &info));       
543       }
544       
545       free(iv);
546     }
547   free(ctx);
548   free(key);
549 }
550
551 /* Try to get accurate cycle times for assembler functions. */
552 #if WITH_CYCLE_COUNTER
553 static int
554 compare_double(const void *ap, const void *bp)
555 {
556   double a = *(const double *) ap;
557   double b = *(const double *) bp;
558   if (a < b)
559     return -1;
560   else if (a > b)
561     return 1;
562   else
563     return 0;
564 }
565
566 #define TIME_CYCLES(t, code) do {                               \
567   double tc_count[5];                                           \
568   uint32_t tc_start_lo, tc_start_hi, tc_end_lo, tc_end_hi;      \
569   unsigned tc_i, tc_j;                                          \
570   for (tc_j = 0; tc_j < 5; tc_j++)                              \
571     {                                                           \
572       tc_i = 0;                                                 \
573       GET_CYCLE_COUNTER(tc_start_hi, tc_start_lo);              \
574       for (; tc_i < BENCH_ITERATIONS; tc_i++)                   \
575         { code; }                                               \
576                                                                 \
577       GET_CYCLE_COUNTER(tc_end_hi, tc_end_lo);                  \
578                                                                 \
579       tc_end_hi -= (tc_start_hi + (tc_start_lo > tc_end_lo));   \
580       tc_end_lo -= tc_start_lo;                                 \
581                                                                 \
582       tc_count[tc_j] = ldexp(tc_end_hi, 32) + tc_end_lo;        \
583     }                                                           \
584   qsort(tc_count, 5, sizeof(double), compare_double);           \
585   (t) = tc_count[2] / BENCH_ITERATIONS;                         \
586 } while (0)
587
588 static void
589 bench_sha1_compress(void)
590 {
591   uint32_t state[_SHA1_DIGEST_LENGTH];
592   uint8_t data[SHA1_DATA_SIZE];
593   double t;
594
595   TIME_CYCLES (t, _nettle_sha1_compress(state, data));
596
597   printf("sha1_compress: %.2f cycles\n", t);  
598 }
599
600 static void
601 bench_salsa20_core(void)
602 {
603   uint32_t state[_SALSA20_INPUT_LENGTH];
604   double t;
605
606   TIME_CYCLES (t, _nettle_salsa20_core(state, state, 20));
607   printf("salsa20_core: %.2f cycles\n", t);  
608 }
609
610 static void
611 bench_sha3_permute(void)
612 {
613   struct sha3_state state;
614   double t;
615
616   TIME_CYCLES (t, sha3_permute (&state));
617   printf("sha3_permute: %.2f cycles (%.2f / round)\n", t, t / 24.0);
618 }
619 #else
620 #define bench_sha1_compress()
621 #define bench_salsa20_core()
622 #define bench_sha3_permute()
623 #endif
624
625 #if WITH_OPENSSL
626 # define OPENSSL(x) x,
627 #else
628 # define OPENSSL(x)
629 #endif
630
631 int
632 main(int argc, char **argv)
633 {
634   unsigned i;
635   int c;
636   const char *alg;
637
638   const struct nettle_hash *hashes[] =
639     {
640       &nettle_md2, &nettle_md4, &nettle_md5,
641       OPENSSL(&nettle_openssl_md5)
642       &nettle_sha1, OPENSSL(&nettle_openssl_sha1)
643       &nettle_sha224, &nettle_sha256,
644       &nettle_sha384, &nettle_sha512,
645       &nettle_sha3_224, &nettle_sha3_256,
646       &nettle_sha3_384, &nettle_sha3_512,
647       &nettle_ripemd160, &nettle_gosthash94,
648       NULL
649     };
650
651   const struct nettle_cipher *ciphers[] =
652     {
653       &nettle_aes128, &nettle_aes192, &nettle_aes256,
654       OPENSSL(&nettle_openssl_aes128)
655       OPENSSL(&nettle_openssl_aes192)
656       OPENSSL(&nettle_openssl_aes256)
657       &nettle_arcfour128, OPENSSL(&nettle_openssl_arcfour128)
658       &nettle_blowfish128, OPENSSL(&nettle_openssl_blowfish128)
659       &nettle_camellia128, &nettle_camellia192, &nettle_camellia256,
660       &nettle_cast128, OPENSSL(&nettle_openssl_cast128)
661       &nettle_des, OPENSSL(&nettle_openssl_des)
662       &nettle_des3,
663       &nettle_serpent256,
664       &nettle_twofish128, &nettle_twofish192, &nettle_twofish256,
665       &nettle_salsa20, &nettle_salsa20r12,
666       NULL
667     };
668
669   enum { OPT_HELP = 300 };
670   static const struct option options[] =
671     {
672       /* Name, args, flag, val */
673       { "help", no_argument, NULL, OPT_HELP },
674       { "clock-frequency", required_argument, NULL, 'f' },
675       { NULL, 0, NULL, 0 }
676     };
677   
678   while ( (c = getopt_long(argc, argv, "f:", options, NULL)) != -1)
679     switch (c)
680       {
681       case 'f':
682         frequency = atof(optarg);
683         if (frequency > 0.0)
684           break;
685
686       case OPT_HELP:
687         printf("Usage: nettle-benchmark [-f clock frequency] [alg]\n");
688         return EXIT_SUCCESS;
689
690       case '?':
691         return EXIT_FAILURE;
692
693       default:
694         abort();
695     }
696
697   alg = argv[optind];
698
699   time_init();
700   bench_sha1_compress();
701   bench_salsa20_core();
702   bench_sha3_permute();
703   printf("\n");
704   time_overhead();
705
706   header();
707
708   if (!alg || strstr ("memxor", alg))
709     {
710       time_memxor();
711       printf("\n");
712     }
713   
714   for (i = 0; hashes[i]; i++)
715     if (!alg || strstr(hashes[i]->name, alg))
716       time_hash(hashes[i]);
717
718   if (!alg || strstr ("umac", alg))
719     time_umac();
720
721   for (i = 0; ciphers[i]; i++)
722     if (!alg || strstr(ciphers[i]->name, alg))
723       time_cipher(ciphers[i]);
724
725   if (!alg || strstr ("gcm", alg))
726     {
727       printf("\n");
728       time_gcm();
729     }
730
731   return 0;
732 }