nettle-benchmark.c 9.15 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 392
      &nettle_sha224, &nettle_sha256,
      &nettle_sha384, &nettle_sha512,
393 394 395
      NULL
    };

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

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
  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();
    }

428 429
  bench_sha1_compress();

430 431
  header();

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