nettle-benchmark.c 11.4 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
#include "des.h"
46
#include "memxor.h"
Niels Möller's avatar
Niels Möller committed
47
#include "serpent.h"
48
#include "sha.h"
Niels Möller's avatar
Niels Möller committed
49 50
#include "twofish.h"

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

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

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

58
/* Process BENCH_BLOCK bytes at a time, for BENCH_INTERVAL seconds. */
59
#define BENCH_BLOCK 10240
60
#define BENCH_INTERVAL 0.1
Niels Möller's avatar
Niels Möller committed
61

62 63
/* FIXME: Proper configure test for rdtsc? */
#ifndef WITH_CYCLE_COUNTER
64
# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
65 66 67 68 69 70 71
#  define WITH_CYCLE_COUNTER 1
# else
#  define WITH_CYCLE_COUNTER 0
# endif
#endif

#if WITH_CYCLE_COUNTER
72
# if defined(__i386__)
73 74 75 76 77 78 79 80 81
#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")
82 83 84 85 86 87 88 89 90 91 92
# elif defined(__x86_64__)
#define GET_CYCLE_COUNTER(hi, lo)		\
  __asm__("xorl %%eax,%%eax\n"			\
	  "mov %%rbx, %%r10\n"			\
	  "cpuid\n"				\
	  "rdtsc\n"				\
	  "mov %%r10, %%rbx\n"			\
	  : "=a" (lo), "=d" (hi)		\
	  : /* No inputs. */			\
	  : "%r10", "%rcx", "cc")
# endif
93 94 95
#define BENCH_ITERATIONS 10
#endif

96 97
static double overhead = 0.0; 

98
/* Returns second per function call */
99 100 101
static double
time_function(void (*f)(void *arg), void *arg)
{
102
  unsigned ncalls;
103 104
  double elapsed;

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
#if HAVE_CLOCK_GETTIME && defined CLOCK_PROCESS_CPUTIME_ID
  struct timespec before;
  struct timespec after;
  struct timespec done;

  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &before);
  done = before;
  done.tv_nsec += BENCH_INTERVAL * 1e9;
  if (done.tv_nsec >= 1000000000L)
    {
      done.tv_nsec -= 1000000000L;
      done.tv_sec ++;
    }
  ncalls = 0;

  do 
    {
      f(arg);
      clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &after);
      ncalls++;
    }
  while (after.tv_sec < done.tv_sec
	 || (after.tv_nsec < done.tv_nsec && after.tv_sec == done.tv_sec));
128 129 130 131

  elapsed = after.tv_sec - before.tv_sec
	     + 1e-9 * (after.tv_nsec - before.tv_nsec);

132
#else /* !HAVE_CLOCK_GETTIME */
133 134
  clock_t before;
  clock_t after;
135 136
  clock_t done;
  
137
  before = clock();
138
  done = before + BENCH_INTERVAL * CLOCKS_PER_SEC;
139
  ncalls = 0;
140
  
141 142 143 144 145 146 147
  do 
    {
      f(arg);
      after = clock();
      ncalls++;
    }
  while (after < done);
148 149

  elapsed = (double)(after - before) / CLOCKS_PER_SEC;
150
#endif /* !HAVE_CLOCK_GETTIME */
151 152 153 154 155 156 157
  return elapsed / ncalls - overhead;
}

static void
bench_nothing(void *arg UNUSED)
{
  return;
158 159
}

160 161 162 163 164 165 166 167 168 169 170 171 172
struct bench_memxor_info
{
  uint8_t *dst;
  const uint8_t *src;
};

static void
bench_memxor(void *arg)
{
  struct bench_memxor_info *info = arg;
  memxor (info->dst, info->src, BENCH_BLOCK);
}

173 174 175
struct bench_hash_info
{
  void *ctx;
176
  nettle_hash_update_func *update;
177 178 179 180 181 182 183 184 185 186
  const uint8_t *data;
};

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

187 188 189
struct bench_cipher_info
{
  void *ctx;
190
  nettle_crypt_func *crypt;
191 192 193 194 195 196 197
  uint8_t *data;
};

static void
bench_cipher(void *arg)
{
  struct bench_cipher_info *info = arg;
198
  info->crypt(info->ctx, BENCH_BLOCK, info->data, info->data);
199 200 201 202 203
}

struct bench_cbc_info
{
  void *ctx;
204
  nettle_crypt_func *crypt;
205
 
206
  uint8_t *data;
207
  
208 209 210 211 212 213 214 215
  unsigned block_size;
  uint8_t *iv;
};

static void
bench_cbc_encrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
216 217 218
  cbc_encrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
219 220 221 222 223 224
}

static void
bench_cbc_decrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
225 226 227
  cbc_decrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
228 229 230 231 232 233 234
}

/* 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
235
  for (i = j = 0; i<BENCH_BLOCK;  i++)
236 237 238 239 240 241 242 243
    {
      if (j*j < i)
	j++;
      data[i] = j;
    }
}

static void
Niels Möller's avatar
Niels Möller committed
244 245
init_key(unsigned length,
         uint8_t *key)
246
{
Niels Möller's avatar
Niels Möller committed
247 248 249
  unsigned i;
  for (i = 0; i<length; i++)
    key[i] = i;
250 251
}

252 253 254 255 256
static void
header(void)
{
  printf("%18s %11s Mbyte/s%s\n",
	 "Algorithm", "mode", 
Niels Möller's avatar
Niels Möller committed
257
	 frequency > 0.0 ? " cycles/byte cycles/block" : "");  
258 259
}

Niels Möller's avatar
Niels Möller committed
260
static void
Niels Möller's avatar
Niels Möller committed
261
display(const char *name, const char *mode, unsigned block_size,
262
	double time)
Niels Möller's avatar
Niels Möller committed
263
{
264
  printf("%18s %11s %7.2f",
Niels Möller's avatar
Niels Möller committed
265
	 name, mode,
266
	 BENCH_BLOCK / (time * 1048576.0));
267
  if (frequency > 0.0)
Niels Möller's avatar
Niels Möller committed
268
    {
269
      printf(" %11.2f", time * frequency / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
270
      if (block_size > 0)
271
	printf(" %12.2f", time * frequency * block_size / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
272
    }
273
  printf("\n");
Niels Möller's avatar
Niels Möller committed
274 275
}

276 277 278 279 280 281 282 283 284 285 286 287 288
static void *
xalloc(size_t size)
{
  void *p = malloc(size);
  if (!p)
    {
      fprintf(stderr, "Virtual memory exhausted.\n");
      abort();
    }

  return p;
}

289 290 291 292 293 294 295 296 297 298 299 300
static void
time_overhead(void)
{
  overhead = time_function(bench_nothing, NULL);
  printf("benchmark call overhead: %7f us", overhead * 1e6);
  if (frequency > 0.0)
    printf("%7.2f cycles\n", overhead * frequency);
  printf("\n");  
}



301 302 303 304
static void
time_memxor(void)
{
  struct bench_memxor_info info;
305 306
  uint8_t src[BENCH_BLOCK + sizeof(long)];
  uint8_t dst[BENCH_BLOCK];
307 308 309 310

  info.src = src;
  info.dst = dst;

311 312
  display ("memxor", "aligned", sizeof(unsigned long),
	   time_function(bench_memxor, &info));
313
  info.src++;
314 315
  display ("memxor", "unaligned", sizeof(unsigned long),
	   time_function(bench_memxor, &info));  
316 317
}

318 319 320 321 322
static void
time_hash(const struct nettle_hash *hash)
{
  static uint8_t data[BENCH_BLOCK];
  struct bench_hash_info info;
323

324
  info.ctx = xalloc(hash->context_size); 
325 326 327 328 329 330
  info.update = hash->update;
  info.data = data;

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

Niels Möller's avatar
Niels Möller committed
331
  display(hash->name, "update", hash->block_size,
332
	  time_function(bench_hash, &info));
333 334

  free(info.ctx);
335 336
}

Niels Möller's avatar
Niels Möller committed
337
static void
338
time_cipher(const struct nettle_cipher *cipher)
Niels Möller's avatar
Niels Möller committed
339
{
340 341
  void *ctx = xalloc(cipher->context_size);
  uint8_t *key = xalloc(cipher->key_size);
Niels Möller's avatar
Niels Möller committed
342

343
  static uint8_t data[BENCH_BLOCK];
Niels Möller's avatar
Niels Möller committed
344 345 346 347

  printf("\n");
  
  init_data(data);
348 349

  {
Niels Möller's avatar
Niels Möller committed
350 351 352 353 354
    /* 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;
355
    
Niels Möller's avatar
Niels Möller committed
356
    init_key(cipher->key_size, key);
357
    cipher->set_encrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
358

Niels Möller's avatar
Niels Möller committed
359
    display(cipher->name, "ECB encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
360
	    time_function(bench_cipher, &info));
361
  }
Niels Möller's avatar
Niels Möller committed
362
  
363
  {
Niels Möller's avatar
Niels Möller committed
364 365 366 367
    struct bench_cipher_info info;
    info.ctx = ctx;
    info.crypt = cipher->decrypt;
    info.data = data;
368
    
Niels Möller's avatar
Niels Möller committed
369
    init_key(cipher->key_size, key);
370
    cipher->set_decrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
371

Niels Möller's avatar
Niels Möller committed
372
    display(cipher->name, "ECB decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
373
	    time_function(bench_cipher, &info));
374 375
  }

Niels Möller's avatar
Niels Möller committed
376 377
  /* 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
378
    {
379
      uint8_t *iv = xalloc(cipher->block_size);
Niels Möller's avatar
Niels Möller committed
380 381 382
      
      /* Do CBC mode */
      {
Niels Möller's avatar
Niels Möller committed
383 384 385 386 387 388
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->encrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
389
    
Niels Möller's avatar
Niels Möller committed
390
        memset(iv, 0, sizeof(iv));
391
    
392
        cipher->set_encrypt_key(ctx, cipher->key_size, key);
393

Niels Möller's avatar
Niels Möller committed
394
	display(cipher->name, "CBC encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
395
		time_function(bench_cbc_encrypt, &info));
Niels Möller's avatar
Niels Möller committed
396
      }
397

Niels Möller's avatar
Niels Möller committed
398
      {
Niels Möller's avatar
Niels Möller committed
399 400 401 402 403 404
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->decrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
405
    
Niels Möller's avatar
Niels Möller committed
406
        memset(iv, 0, sizeof(iv));
407

408
        cipher->set_decrypt_key(ctx, cipher->key_size, key);
409

Niels Möller's avatar
Niels Möller committed
410
	display(cipher->name, "CBC decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
411
		time_function(bench_cbc_decrypt, &info));
Niels Möller's avatar
Niels Möller committed
412
      }
413
      free(iv);
Niels Möller's avatar
Niels Möller committed
414
    }
415 416
  free(ctx);
  free(key);
Niels Möller's avatar
Niels Möller committed
417 418
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
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
467
#if WITH_OPENSSL
468 469 470 471
# define OPENSSL(x) x,
#else
# define OPENSSL(x)
#endif
Niels Möller's avatar
Niels Möller committed
472 473

int
474
main(int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
475 476
{
  unsigned i;
477
  int c;
478
  const char *alg;
479 480 481 482

  const struct nettle_hash *hashes[] =
    {
      &nettle_md2, &nettle_md4, &nettle_md5,
483
      OPENSSL(&nettle_openssl_md5)
484
      &nettle_sha1, OPENSSL(&nettle_openssl_sha1)
Niels Möller's avatar
Niels Möller committed
485 486
      &nettle_sha224, &nettle_sha256,
      &nettle_sha384, &nettle_sha512,
487 488 489
      NULL
    };

490
  const struct nettle_cipher *ciphers[] =
Niels Möller's avatar
Niels Möller committed
491
    {
492
      &nettle_aes128, &nettle_aes192, &nettle_aes256,
Niels Möller's avatar
Niels Möller committed
493 494 495 496 497
      OPENSSL(&nettle_openssl_aes128)
      OPENSSL(&nettle_openssl_aes192)
      OPENSSL(&nettle_openssl_aes256)
      &nettle_arcfour128, OPENSSL(&nettle_openssl_arcfour128)
      &nettle_blowfish128, OPENSSL(&nettle_openssl_blowfish128)
Niels Möller's avatar
Niels Möller committed
498
      &nettle_camellia128, &nettle_camellia192, &nettle_camellia256,
499 500 501
      &nettle_cast128, OPENSSL(&nettle_openssl_cast128)
      &nettle_des, OPENSSL(&nettle_openssl_des)
      &nettle_des3,
502 503
      &nettle_serpent256,
      &nettle_twofish128, &nettle_twofish192, &nettle_twofish256,
504
      NULL
Niels Möller's avatar
Niels Möller committed
505
    };
506

507 508 509 510 511 512 513 514 515
  while ( (c = getopt(argc, argv, "f:")) != -1)
    switch (c)
      {
      case 'f':
	frequency = atof(optarg);
	if (frequency > 0.0)
	  break;

      case ':': case '?':
516
	fprintf(stderr, "Usage: nettle-benchmark [-f clock frequency] [alg]\n");
517 518 519 520 521 522
	return EXIT_FAILURE;

      default:
	abort();
    }

523
  alg = argv[optind];
524

525 526
  bench_sha1_compress();

527 528
  time_overhead();

529 530
  header();

531 532 533 534 535
  if (!alg || strstr ("memxor", alg))
    {
      time_memxor();
      printf("\n");
    }
536
  
537 538 539 540 541
  for (i = 0; hashes[i]; i++)
    {
      if (!alg || strstr(hashes[i]->name, alg))
	time_hash(hashes[i]);
    }
542
  for (i = 0; ciphers[i]; i++)
543 544 545
    if (!alg || strstr(ciphers[i]->name, alg))
      time_cipher(ciphers[i]);

546 547
  return 0;
}