nettle-benchmark.c 9.11 KB
Newer Older
1
2
3
4
5
6
7
8
/* nettle-benchmark.c
 *
 * Tries the performance of the various algorithms.
 *
 */
 
/* nettle, low-level cryptographics library
 *
Niels Möller's avatar
Niels Möller committed
9
 * Copyright (C) 2001, 2010 Niels Mller
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *  
 * The nettle library 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.
 * 
 * The nettle library 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 the nettle library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

27
28
#if HAVE_CONFIG_H
# include "config.h"
29
30
31
32
#endif

#include <assert.h>
#include <errno.h>
33
#include <math.h>
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <time.h>
39

40
#include "aes.h"
Niels Möller's avatar
Niels Möller committed
41
42
43
#include "arcfour.h"
#include "blowfish.h"
#include "cast128.h"
Niels Möller's avatar
Niels Möller committed
44
#include "cbc.h"
Niels Möller's avatar
Niels Möller committed
45
46
#include "des.h"
#include "serpent.h"
47
#include "sha.h"
Niels Möller's avatar
Niels Möller committed
48
49
#include "twofish.h"

50
51
52
#include "nettle-meta.h"
#include "nettle-internal.h"

Niels Möller's avatar
Niels Möller committed
53
#include "getopt.h"
54

55
static double frequency = 0.0;
Niels Möller's avatar
Niels Möller committed
56

57
58
59
/* Process BENCH_BLOCK bytes at a time, for BENCH_INTERVAL clocks. */
#define BENCH_BLOCK 10240
#define BENCH_INTERVAL (CLOCKS_PER_SEC / 4)
60

Niels Möller's avatar
Niels Möller committed
61
62
63
/* Total MB:s, for MB/s figures. */
#define BENCH_TOTAL 10.0

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* FIXME: Proper configure test for rdtsc? */
#ifndef WITH_CYCLE_COUNTER
# if defined(__GNUC__) && defined(__i386__)
#  define WITH_CYCLE_COUNTER 1
# else
#  define WITH_CYCLE_COUNTER 0
# endif
#endif

#if WITH_CYCLE_COUNTER
#define GET_CYCLE_COUNTER(hi, lo)		\
  __asm__("xorl %%eax,%%eax\n"			\
	  "movl %%ebx, %%edi\n"			\
	  "cpuid\n"				\
	  "rdtsc\n"				\
	  "movl %%edi, %%ebx\n"			\
	  : "=a" (lo), "=d" (hi)		\
	  : /* No inputs. */			\
	  : "%edi", "%ecx", "cc")
#define BENCH_ITERATIONS 10
#endif

86
/* Returns second per function call */
87
88
89
90
91
static double
time_function(void (*f)(void *arg), void *arg)
{
  clock_t before;
  clock_t after;
92
93
94
  clock_t done;
  unsigned ncalls;
  
95
  before = clock();
96
97
  done = before + BENCH_INTERVAL;
  ncalls = 0;
98
  
99
100
101
102
103
104
105
  do 
    {
      f(arg);
      after = clock();
      ncalls++;
    }
  while (after < done);
106
  
107
  return ((double)(after - before)) / CLOCKS_PER_SEC / ncalls;
108
109
}

110
111
112
struct bench_hash_info
{
  void *ctx;
113
  nettle_hash_update_func *update;
114
115
116
117
118
119
120
121
122
123
  const uint8_t *data;
};

static void
bench_hash(void *arg)
{
  struct bench_hash_info *info = arg;
  info->update(info->ctx, BENCH_BLOCK, info->data);
}

124
125
126
struct bench_cipher_info
{
  void *ctx;
127
  nettle_crypt_func *crypt;
128
129
130
131
132
133
134
  uint8_t *data;
};

static void
bench_cipher(void *arg)
{
  struct bench_cipher_info *info = arg;
135
  info->crypt(info->ctx, BENCH_BLOCK, info->data, info->data);
136
137
138
139
140
}

struct bench_cbc_info
{
  void *ctx;
141
  nettle_crypt_func *crypt;
142
 
143
  uint8_t *data;
144
  
145
146
147
148
149
150
151
152
  unsigned block_size;
  uint8_t *iv;
};

static void
bench_cbc_encrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
153
154
155
  cbc_encrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
156
157
158
159
160
161
}

static void
bench_cbc_decrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
162
163
164
  cbc_decrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
165
166
167
168
169
170
171
}

/* Set data[i] = floor(sqrt(i)) */
static void
init_data(uint8_t *data)
{
  unsigned i,j;
Niels Möller's avatar
Niels Möller committed
172
  for (i = j = 0; i<BENCH_BLOCK;  i++)
173
174
175
176
177
178
179
180
    {
      if (j*j < i)
	j++;
      data[i] = j;
    }
}

static void
Niels Möller's avatar
Niels Möller committed
181
182
init_key(unsigned length,
         uint8_t *key)
183
{
Niels Möller's avatar
Niels Möller committed
184
185
186
  unsigned i;
  for (i = 0; i<length; i++)
    key[i] = i;
187
188
}

189
190
191
192
193
static void
header(void)
{
  printf("%18s %11s Mbyte/s%s\n",
	 "Algorithm", "mode", 
Niels Möller's avatar
Niels Möller committed
194
	 frequency > 0.0 ? " cycles/byte cycles/block" : "");  
195
196
}

Niels Möller's avatar
Niels Möller committed
197
static void
Niels Möller's avatar
Niels Möller committed
198
display(const char *name, const char *mode, unsigned block_size,
199
	double time)
Niels Möller's avatar
Niels Möller committed
200
{
201
  printf("%18s %11s %7.2f",
Niels Möller's avatar
Niels Möller committed
202
	 name, mode,
203
	 BENCH_BLOCK / (time * 1048576.0));
204
  if (frequency > 0.0)
Niels Möller's avatar
Niels Möller committed
205
    {
206
      printf(" %11.2f", time * frequency / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
207
      if (block_size > 0)
208
	printf(" %12.2f", time * frequency * block_size / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
209
    }
210
  printf("\n");
Niels Möller's avatar
Niels Möller committed
211
212
}

213
214
215
216
217
218
219
220
221
222
223
224
225
static void *
xalloc(size_t size)
{
  void *p = malloc(size);
  if (!p)
    {
      fprintf(stderr, "Virtual memory exhausted.\n");
      abort();
    }

  return p;
}

226
227
228
229
230
static void
time_hash(const struct nettle_hash *hash)
{
  static uint8_t data[BENCH_BLOCK];
  struct bench_hash_info info;
231
  info.ctx = xalloc(hash->context_size); 
232
233
234
235
236
237
  info.update = hash->update;
  info.data = data;

  init_data(data);
  hash->init(info.ctx);

Niels Möller's avatar
Niels Möller committed
238
  display(hash->name, "update", hash->block_size,
239
	  time_function(bench_hash, &info));
240
241

  free(info.ctx);
242
243
}

Niels Möller's avatar
Niels Möller committed
244
static void
245
time_cipher(const struct nettle_cipher *cipher)
Niels Möller's avatar
Niels Möller committed
246
{
247
248
  void *ctx = xalloc(cipher->context_size);
  uint8_t *key = xalloc(cipher->key_size);
Niels Möller's avatar
Niels Möller committed
249

250
  static uint8_t data[BENCH_BLOCK];
Niels Möller's avatar
Niels Möller committed
251
252
253
254

  printf("\n");
  
  init_data(data);
255
256

  {
Niels Möller's avatar
Niels Möller committed
257
258
259
260
261
    /* Decent initializers are a GNU extension, so don't use it here. */
    struct bench_cipher_info info;
    info.ctx = ctx;
    info.crypt = cipher->encrypt;
    info.data = data;
262
    
Niels Möller's avatar
Niels Möller committed
263
    init_key(cipher->key_size, key);
264
    cipher->set_encrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
265

Niels Möller's avatar
Niels Möller committed
266
    display(cipher->name, "ECB encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
267
	    time_function(bench_cipher, &info));
268
  }
Niels Möller's avatar
Niels Möller committed
269
  
270
  {
Niels Möller's avatar
Niels Möller committed
271
272
273
274
    struct bench_cipher_info info;
    info.ctx = ctx;
    info.crypt = cipher->decrypt;
    info.data = data;
275
    
Niels Möller's avatar
Niels Möller committed
276
    init_key(cipher->key_size, key);
277
    cipher->set_decrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
278

Niels Möller's avatar
Niels Möller committed
279
    display(cipher->name, "ECB decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
280
	    time_function(bench_cipher, &info));
281
282
  }

Niels Möller's avatar
Niels Möller committed
283
284
  /* Don't use nettle cbc to benchmark openssl ciphers */
  if (cipher->block_size && cipher->name[0] != 'o')
Niels Möller's avatar
Niels Möller committed
285
    {
286
      uint8_t *iv = xalloc(cipher->block_size);
Niels Möller's avatar
Niels Möller committed
287
288
289
      
      /* Do CBC mode */
      {
Niels Möller's avatar
Niels Möller committed
290
291
292
293
294
295
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->encrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
296
    
Niels Möller's avatar
Niels Möller committed
297
        memset(iv, 0, sizeof(iv));
298
    
299
        cipher->set_encrypt_key(ctx, cipher->key_size, key);
300

Niels Möller's avatar
Niels Möller committed
301
	display(cipher->name, "CBC encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
302
		time_function(bench_cbc_encrypt, &info));
Niels Möller's avatar
Niels Möller committed
303
      }
304

Niels Möller's avatar
Niels Möller committed
305
      {
Niels Möller's avatar
Niels Möller committed
306
307
308
309
310
311
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->decrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
312
    
Niels Möller's avatar
Niels Möller committed
313
        memset(iv, 0, sizeof(iv));
314

315
        cipher->set_decrypt_key(ctx, cipher->key_size, key);
316

Niels Möller's avatar
Niels Möller committed
317
	display(cipher->name, "CBC decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
318
		time_function(bench_cbc_decrypt, &info));
Niels Möller's avatar
Niels Möller committed
319
      }
320
      free(iv);
Niels Möller's avatar
Niels Möller committed
321
    }
322
323
  free(ctx);
  free(key);
Niels Möller's avatar
Niels Möller committed
324
325
}

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
static int
compare_double(const void *ap, const void *bp)
{
  double a = *(const double *) ap;
  double b = *(const double *) bp;
  if (a < b)
    return -1;
  else if (a > b)
    return 1;
  else
    return 0;
}

/* Try to get accurate cycle times for assembler functions. */
static void
bench_sha1_compress(void)
{
#if WITH_CYCLE_COUNTER
  uint32_t state[_SHA1_DIGEST_LENGTH];
  uint8_t data[BENCH_ITERATIONS * SHA1_DATA_SIZE];
  uint32_t start_lo, start_hi, end_lo, end_hi;

  double count[5];
  
  uint8_t *p;
  unsigned i, j;

  for (j = 0; j < 5; j++)
    {
      i = 0;
      p = data;
      GET_CYCLE_COUNTER(start_hi, start_lo);
      for (; i < BENCH_ITERATIONS; i++, p += SHA1_DATA_SIZE)
	_nettle_sha1_compress(state, p);

      GET_CYCLE_COUNTER(end_hi, end_lo);

      end_hi -= (start_hi + (start_lo > end_lo));
      end_lo -= start_lo;

      count[j] = ldexp(end_hi, 32) + end_lo;
    }

  qsort(count, 5, sizeof(double), compare_double);
  printf("sha1_compress: %.2f cycles\n\n", count[2] / BENCH_ITERATIONS);  
#endif
}

Niels Möller's avatar
Niels Möller committed
374
#if WITH_OPENSSL
375
376
377
378
# define OPENSSL(x) x,
#else
# define OPENSSL(x)
#endif
Niels Möller's avatar
Niels Möller committed
379
380

int
381
main(int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
382
383
{
  unsigned i;
384
  int c;
385
386
387
388

  const struct nettle_hash *hashes[] =
    {
      &nettle_md2, &nettle_md4, &nettle_md5,
389
      OPENSSL(&nettle_openssl_md5)
390
      &nettle_sha1, OPENSSL(&nettle_openssl_sha1)
Niels Möller's avatar
Niels Möller committed
391
      &nettle_sha256, &nettle_sha512,
392
393
394
      NULL
    };

395
  const struct nettle_cipher *ciphers[] =
Niels Möller's avatar
Niels Möller committed
396
    {
397
      &nettle_aes128, &nettle_aes192, &nettle_aes256,
Niels Möller's avatar
Niels Möller committed
398
399
400
401
402
      OPENSSL(&nettle_openssl_aes128)
      OPENSSL(&nettle_openssl_aes192)
      OPENSSL(&nettle_openssl_aes256)
      &nettle_arcfour128, OPENSSL(&nettle_openssl_arcfour128)
      &nettle_blowfish128, OPENSSL(&nettle_openssl_blowfish128)
403
404
405
      &nettle_cast128, OPENSSL(&nettle_openssl_cast128)
      &nettle_des, OPENSSL(&nettle_openssl_des)
      &nettle_des3,
406
407
      &nettle_serpent256,
      &nettle_twofish128, &nettle_twofish192, &nettle_twofish256,
408
      NULL
Niels Möller's avatar
Niels Möller committed
409
    };
410

411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  while ( (c = getopt(argc, argv, "f:")) != -1)
    switch (c)
      {
      case 'f':
	frequency = atof(optarg);
	if (frequency > 0.0)
	  break;

      case ':': case '?':
	fprintf(stderr, "Usage: nettle-benchmark [-f clock frequency]\n");
	return EXIT_FAILURE;

      default:
	abort();
    }

427
428
  bench_sha1_compress();

429
430
  header();

431
432
433
  for (i = 0; hashes[i]; i++)
    time_hash(hashes[i]);
  
434
  for (i = 0; ciphers[i]; i++)
435
    time_cipher(ciphers[i]);
Niels Möller's avatar
Niels Möller committed
436
  
437
438
  return 0;
}