Initialize Tizen 2.3
[external/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., 59 Temple Place - Suite 330, Boston,
24  * MA 02111-1307, 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 <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <time.h>
39
40 #include "aes.h"
41 #include "arcfour.h"
42 #include "blowfish.h"
43 #include "cast128.h"
44 #include "cbc.h"
45 #include "des.h"
46 #include "serpent.h"
47 #include "sha.h"
48 #include "twofish.h"
49
50 #include "nettle-meta.h"
51 #include "nettle-internal.h"
52
53 #include "getopt.h"
54
55 static double frequency = 0.0;
56
57 /* Process BENCH_BLOCK bytes at a time, for BENCH_INTERVAL clocks. */
58 #define BENCH_BLOCK 10240
59 #define BENCH_INTERVAL (CLOCKS_PER_SEC / 4)
60
61 /* Total MB:s, for MB/s figures. */
62 #define BENCH_TOTAL 10.0
63
64 /* FIXME: Proper configure test for rdtsc? */
65 #ifndef WITH_CYCLE_COUNTER
66 # if defined(__GNUC__) && defined(__i386__)
67 #  define WITH_CYCLE_COUNTER 1
68 # else
69 #  define WITH_CYCLE_COUNTER 0
70 # endif
71 #endif
72
73 #if WITH_CYCLE_COUNTER
74 #define GET_CYCLE_COUNTER(hi, lo)               \
75   __asm__("xorl %%eax,%%eax\n"                  \
76           "movl %%ebx, %%edi\n"                 \
77           "cpuid\n"                             \
78           "rdtsc\n"                             \
79           "movl %%edi, %%ebx\n"                 \
80           : "=a" (lo), "=d" (hi)                \
81           : /* No inputs. */                    \
82           : "%edi", "%ecx", "cc")
83 #define BENCH_ITERATIONS 10
84 #endif
85
86 /* Returns second per function call */
87 static double
88 time_function(void (*f)(void *arg), void *arg)
89 {
90   clock_t before;
91   clock_t after;
92   clock_t done;
93   unsigned ncalls;
94   
95   before = clock();
96   done = before + BENCH_INTERVAL;
97   ncalls = 0;
98   
99   do 
100     {
101       f(arg);
102       after = clock();
103       ncalls++;
104     }
105   while (after < done);
106   
107   return ((double)(after - before)) / CLOCKS_PER_SEC / ncalls;
108 }
109
110 struct bench_hash_info
111 {
112   void *ctx;
113   nettle_hash_update_func *update;
114   const uint8_t *data;
115 };
116
117 static void
118 bench_hash(void *arg)
119 {
120   struct bench_hash_info *info = arg;
121   info->update(info->ctx, BENCH_BLOCK, info->data);
122 }
123
124 struct bench_cipher_info
125 {
126   void *ctx;
127   nettle_crypt_func *crypt;
128   uint8_t *data;
129 };
130
131 static void
132 bench_cipher(void *arg)
133 {
134   struct bench_cipher_info *info = arg;
135   info->crypt(info->ctx, BENCH_BLOCK, info->data, info->data);
136 }
137
138 struct bench_cbc_info
139 {
140   void *ctx;
141   nettle_crypt_func *crypt;
142  
143   uint8_t *data;
144   
145   unsigned block_size;
146   uint8_t *iv;
147 };
148
149 static void
150 bench_cbc_encrypt(void *arg)
151 {
152   struct bench_cbc_info *info = arg;
153   cbc_encrypt(info->ctx, info->crypt,
154               info->block_size, info->iv,
155               BENCH_BLOCK, info->data, info->data);
156 }
157
158 static void
159 bench_cbc_decrypt(void *arg)
160 {
161   struct bench_cbc_info *info = arg;
162   cbc_decrypt(info->ctx, info->crypt,
163               info->block_size, info->iv,
164               BENCH_BLOCK, info->data, info->data);
165 }
166
167 /* Set data[i] = floor(sqrt(i)) */
168 static void
169 init_data(uint8_t *data)
170 {
171   unsigned i,j;
172   for (i = j = 0; i<BENCH_BLOCK;  i++)
173     {
174       if (j*j < i)
175         j++;
176       data[i] = j;
177     }
178 }
179
180 static void
181 init_key(unsigned length,
182          uint8_t *key)
183 {
184   unsigned i;
185   for (i = 0; i<length; i++)
186     key[i] = i;
187 }
188
189 static void
190 header(void)
191 {
192   printf("%18s %11s Mbyte/s%s\n",
193          "Algorithm", "mode", 
194          frequency > 0.0 ? " cycles/byte cycles/block" : "");  
195 }
196
197 static void
198 display(const char *name, const char *mode, unsigned block_size,
199         double time)
200 {
201   printf("%18s %11s %7.2f",
202          name, mode,
203          BENCH_BLOCK / (time * 1048576.0));
204   if (frequency > 0.0)
205     {
206       printf(" %11.2f", time * frequency / BENCH_BLOCK);
207       if (block_size > 0)
208         printf(" %12.2f", time * frequency * block_size / BENCH_BLOCK);
209     }
210   printf("\n");
211 }
212
213 static void *
214 xalloc(size_t size)
215 {
216   void *p = malloc(size);
217   if (!p)
218     {
219       fprintf(stderr, "Virtual memory exhausted.\n");
220       abort();
221     }
222
223   return p;
224 }
225
226 static void
227 time_hash(const struct nettle_hash *hash)
228 {
229   static uint8_t data[BENCH_BLOCK];
230   struct bench_hash_info info;
231   info.ctx = xalloc(hash->context_size); 
232   info.update = hash->update;
233   info.data = data;
234
235   init_data(data);
236   hash->init(info.ctx);
237
238   display(hash->name, "update", hash->block_size,
239           time_function(bench_hash, &info));
240
241   free(info.ctx);
242 }
243
244 static void
245 time_cipher(const struct nettle_cipher *cipher)
246 {
247   void *ctx = xalloc(cipher->context_size);
248   uint8_t *key = xalloc(cipher->key_size);
249
250   static uint8_t data[BENCH_BLOCK];
251
252   printf("\n");
253   
254   init_data(data);
255
256   {
257     /* Decent initializers are a GNU extension, so don't use it here. */
258     struct bench_cipher_info info;
259     info.ctx = ctx;
260     info.crypt = cipher->encrypt;
261     info.data = data;
262     
263     init_key(cipher->key_size, key);
264     cipher->set_encrypt_key(ctx, cipher->key_size, key);
265
266     display(cipher->name, "ECB encrypt", cipher->block_size,
267             time_function(bench_cipher, &info));
268   }
269   
270   {
271     struct bench_cipher_info info;
272     info.ctx = ctx;
273     info.crypt = cipher->decrypt;
274     info.data = data;
275     
276     init_key(cipher->key_size, key);
277     cipher->set_decrypt_key(ctx, cipher->key_size, key);
278
279     display(cipher->name, "ECB decrypt", cipher->block_size,
280             time_function(bench_cipher, &info));
281   }
282
283   /* Don't use nettle cbc to benchmark openssl ciphers */
284   if (cipher->block_size && cipher->name[0] != 'o')
285     {
286       uint8_t *iv = xalloc(cipher->block_size);
287       
288       /* Do CBC mode */
289       {
290         struct bench_cbc_info info;
291         info.ctx = ctx;
292         info.crypt = cipher->encrypt;
293         info.data = data;
294         info.block_size = cipher->block_size;
295         info.iv = iv;
296     
297         memset(iv, 0, sizeof(iv));
298     
299         cipher->set_encrypt_key(ctx, cipher->key_size, key);
300
301         display(cipher->name, "CBC encrypt", cipher->block_size,
302                 time_function(bench_cbc_encrypt, &info));
303       }
304
305       {
306         struct bench_cbc_info info;
307         info.ctx = ctx;
308         info.crypt = cipher->decrypt;
309         info.data = data;
310         info.block_size = cipher->block_size;
311         info.iv = iv;
312     
313         memset(iv, 0, sizeof(iv));
314
315         cipher->set_decrypt_key(ctx, cipher->key_size, key);
316
317         display(cipher->name, "CBC decrypt", cipher->block_size,
318                 time_function(bench_cbc_decrypt, &info));
319       }
320       free(iv);
321     }
322   free(ctx);
323   free(key);
324 }
325
326 static int
327 compare_double(const void *ap, const void *bp)
328 {
329   double a = *(const double *) ap;
330   double b = *(const double *) bp;
331   if (a < b)
332     return -1;
333   else if (a > b)
334     return 1;
335   else
336     return 0;
337 }
338
339 /* Try to get accurate cycle times for assembler functions. */
340 static void
341 bench_sha1_compress(void)
342 {
343 #if WITH_CYCLE_COUNTER
344   uint32_t state[_SHA1_DIGEST_LENGTH];
345   uint8_t data[BENCH_ITERATIONS * SHA1_DATA_SIZE];
346   uint32_t start_lo, start_hi, end_lo, end_hi;
347
348   double count[5];
349   
350   uint8_t *p;
351   unsigned i, j;
352
353   for (j = 0; j < 5; j++)
354     {
355       i = 0;
356       p = data;
357       GET_CYCLE_COUNTER(start_hi, start_lo);
358       for (; i < BENCH_ITERATIONS; i++, p += SHA1_DATA_SIZE)
359         _nettle_sha1_compress(state, p);
360
361       GET_CYCLE_COUNTER(end_hi, end_lo);
362
363       end_hi -= (start_hi + (start_lo > end_lo));
364       end_lo -= start_lo;
365
366       count[j] = ldexp(end_hi, 32) + end_lo;
367     }
368
369   qsort(count, 5, sizeof(double), compare_double);
370   printf("sha1_compress: %.2f cycles\n\n", count[2] / BENCH_ITERATIONS);  
371 #endif
372 }
373
374 #if WITH_OPENSSL
375 # define OPENSSL(x) x,
376 #else
377 # define OPENSSL(x)
378 #endif
379
380 int
381 main(int argc, char **argv)
382 {
383   unsigned i;
384   int c;
385
386   const struct nettle_hash *hashes[] =
387     {
388       &nettle_md2, &nettle_md4, &nettle_md5,
389       OPENSSL(&nettle_openssl_md5)
390       &nettle_sha1, OPENSSL(&nettle_openssl_sha1)
391       &nettle_sha224, &nettle_sha256,
392       &nettle_sha384, &nettle_sha512,
393       NULL
394     };
395
396   const struct nettle_cipher *ciphers[] =
397     {
398       &nettle_aes128, &nettle_aes192, &nettle_aes256,
399       OPENSSL(&nettle_openssl_aes128)
400       OPENSSL(&nettle_openssl_aes192)
401       OPENSSL(&nettle_openssl_aes256)
402       &nettle_arcfour128, OPENSSL(&nettle_openssl_arcfour128)
403       &nettle_blowfish128, OPENSSL(&nettle_openssl_blowfish128)
404       &nettle_camellia128, &nettle_camellia192, &nettle_camellia256,
405       &nettle_cast128, OPENSSL(&nettle_openssl_cast128)
406       &nettle_des, OPENSSL(&nettle_openssl_des)
407       &nettle_des3,
408       &nettle_serpent256,
409       &nettle_twofish128, &nettle_twofish192, &nettle_twofish256,
410       NULL
411     };
412
413   while ( (c = getopt(argc, argv, "f:")) != -1)
414     switch (c)
415       {
416       case 'f':
417         frequency = atof(optarg);
418         if (frequency > 0.0)
419           break;
420
421       case ':': case '?':
422         fprintf(stderr, "Usage: nettle-benchmark [-f clock frequency]\n");
423         return EXIT_FAILURE;
424
425       default:
426         abort();
427     }
428
429   bench_sha1_compress();
430
431   header();
432
433   for (i = 0; hashes[i]; i++)
434     time_hash(hashes[i]);
435   
436   for (i = 0; ciphers[i]; i++)
437     time_cipher(ciphers[i]);
438   
439   return 0;
440 }