nettle-benchmark.c 10.6 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/* 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

84
/* Returns second per function call */
85 86 87
static double
time_function(void (*f)(void *arg), void *arg)
{
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
  unsigned ncalls;
#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));
  
  return (after.tv_sec - before.tv_sec
	  + 1e-9 * (after.tv_nsec - before.tv_nsec)) / ncalls;
#else /* !HAVE_CLOCK_GETTIME */
116 117
  clock_t before;
  clock_t after;
118 119
  clock_t done;
  
120
  before = clock();
121
  done = before + BENCH_INTERVAL * CLOCKS_PER_SEC;
122
  ncalls = 0;
123
  
124 125 126 127 128 129 130
  do 
    {
      f(arg);
      after = clock();
      ncalls++;
    }
  while (after < done);
131
  
132
  return ((double)(after - before)) / CLOCKS_PER_SEC / ncalls;
133
#endif /* !HAVE_CLOCK_GETTIME */
134 135
}

136 137 138 139 140 141 142 143 144 145 146 147 148
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);
}

149 150 151
struct bench_hash_info
{
  void *ctx;
152
  nettle_hash_update_func *update;
153 154 155 156 157 158 159 160 161 162
  const uint8_t *data;
};

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

163 164 165
struct bench_cipher_info
{
  void *ctx;
166
  nettle_crypt_func *crypt;
167 168 169 170 171 172 173
  uint8_t *data;
};

static void
bench_cipher(void *arg)
{
  struct bench_cipher_info *info = arg;
174
  info->crypt(info->ctx, BENCH_BLOCK, info->data, info->data);
175 176 177 178 179
}

struct bench_cbc_info
{
  void *ctx;
180
  nettle_crypt_func *crypt;
181
 
182
  uint8_t *data;
183
  
184 185 186 187 188 189 190 191
  unsigned block_size;
  uint8_t *iv;
};

static void
bench_cbc_encrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
192 193 194
  cbc_encrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
195 196 197 198 199 200
}

static void
bench_cbc_decrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
201 202 203
  cbc_decrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
204 205 206 207 208 209 210
}

/* 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
211
  for (i = j = 0; i<BENCH_BLOCK;  i++)
212 213 214 215 216 217 218 219
    {
      if (j*j < i)
	j++;
      data[i] = j;
    }
}

static void
Niels Möller's avatar
Niels Möller committed
220 221
init_key(unsigned length,
         uint8_t *key)
222
{
Niels Möller's avatar
Niels Möller committed
223 224 225
  unsigned i;
  for (i = 0; i<length; i++)
    key[i] = i;
226 227
}

228 229 230 231 232
static void
header(void)
{
  printf("%18s %11s Mbyte/s%s\n",
	 "Algorithm", "mode", 
Niels Möller's avatar
Niels Möller committed
233
	 frequency > 0.0 ? " cycles/byte cycles/block" : "");  
234 235
}

Niels Möller's avatar
Niels Möller committed
236
static void
Niels Möller's avatar
Niels Möller committed
237
display(const char *name, const char *mode, unsigned block_size,
238
	double time)
Niels Möller's avatar
Niels Möller committed
239
{
240
  printf("%18s %11s %7.2f",
Niels Möller's avatar
Niels Möller committed
241
	 name, mode,
242
	 BENCH_BLOCK / (time * 1048576.0));
243
  if (frequency > 0.0)
Niels Möller's avatar
Niels Möller committed
244
    {
245
      printf(" %11.2f", time * frequency / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
246
      if (block_size > 0)
247
	printf(" %12.2f", time * frequency * block_size / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
248
    }
249
  printf("\n");
Niels Möller's avatar
Niels Möller committed
250 251
}

252 253 254 255 256 257 258 259 260 261 262 263 264
static void *
xalloc(size_t size)
{
  void *p = malloc(size);
  if (!p)
    {
      fprintf(stderr, "Virtual memory exhausted.\n");
      abort();
    }

  return p;
}

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
static void
time_memxor(void)
{
  struct bench_memxor_info info;
  uint8_t src[BENCH_BLOCK + 1];
  uint8_t dst[BENCH_BLOCK + 1];

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

  display ("xor", "aligned", 1, time_function(bench_memxor, &info));
  info.src++;
  display ("xor", "unaligned", 1, time_function(bench_memxor, &info));  
}

280 281 282 283 284
static void
time_hash(const struct nettle_hash *hash)
{
  static uint8_t data[BENCH_BLOCK];
  struct bench_hash_info info;
285

286
  info.ctx = xalloc(hash->context_size); 
287 288 289 290 291 292
  info.update = hash->update;
  info.data = data;

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

Niels Möller's avatar
Niels Möller committed
293
  display(hash->name, "update", hash->block_size,
294
	  time_function(bench_hash, &info));
295 296

  free(info.ctx);
297 298
}

Niels Möller's avatar
Niels Möller committed
299
static void
300
time_cipher(const struct nettle_cipher *cipher)
Niels Möller's avatar
Niels Möller committed
301
{
302 303
  void *ctx = xalloc(cipher->context_size);
  uint8_t *key = xalloc(cipher->key_size);
Niels Möller's avatar
Niels Möller committed
304

305
  static uint8_t data[BENCH_BLOCK];
Niels Möller's avatar
Niels Möller committed
306 307 308 309

  printf("\n");
  
  init_data(data);
310 311

  {
Niels Möller's avatar
Niels Möller committed
312 313 314 315 316
    /* 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;
317
    
Niels Möller's avatar
Niels Möller committed
318
    init_key(cipher->key_size, key);
319
    cipher->set_encrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
320

Niels Möller's avatar
Niels Möller committed
321
    display(cipher->name, "ECB encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
322
	    time_function(bench_cipher, &info));
323
  }
Niels Möller's avatar
Niels Möller committed
324
  
325
  {
Niels Möller's avatar
Niels Möller committed
326 327 328 329
    struct bench_cipher_info info;
    info.ctx = ctx;
    info.crypt = cipher->decrypt;
    info.data = data;
330
    
Niels Möller's avatar
Niels Möller committed
331
    init_key(cipher->key_size, key);
332
    cipher->set_decrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
333

Niels Möller's avatar
Niels Möller committed
334
    display(cipher->name, "ECB decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
335
	    time_function(bench_cipher, &info));
336 337
  }

Niels Möller's avatar
Niels Möller committed
338 339
  /* 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
340
    {
341
      uint8_t *iv = xalloc(cipher->block_size);
Niels Möller's avatar
Niels Möller committed
342 343 344
      
      /* Do CBC mode */
      {
Niels Möller's avatar
Niels Möller committed
345 346 347 348 349 350
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->encrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
351
    
Niels Möller's avatar
Niels Möller committed
352
        memset(iv, 0, sizeof(iv));
353
    
354
        cipher->set_encrypt_key(ctx, cipher->key_size, key);
355

Niels Möller's avatar
Niels Möller committed
356
	display(cipher->name, "CBC encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
357
		time_function(bench_cbc_encrypt, &info));
Niels Möller's avatar
Niels Möller committed
358
      }
359

Niels Möller's avatar
Niels Möller committed
360
      {
Niels Möller's avatar
Niels Möller committed
361 362 363 364 365 366
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->decrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
367
    
Niels Möller's avatar
Niels Möller committed
368
        memset(iv, 0, sizeof(iv));
369

370
        cipher->set_decrypt_key(ctx, cipher->key_size, key);
371

Niels Möller's avatar
Niels Möller committed
372
	display(cipher->name, "CBC decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
373
		time_function(bench_cbc_decrypt, &info));
Niels Möller's avatar
Niels Möller committed
374
      }
375
      free(iv);
Niels Möller's avatar
Niels Möller committed
376
    }
377 378
  free(ctx);
  free(key);
Niels Möller's avatar
Niels Möller committed
379 380
}

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
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
429
#if WITH_OPENSSL
430 431 432 433
# define OPENSSL(x) x,
#else
# define OPENSSL(x)
#endif
Niels Möller's avatar
Niels Möller committed
434 435

int
436
main(int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
437 438
{
  unsigned i;
439
  int c;
440
  const char *alg;
441 442 443 444

  const struct nettle_hash *hashes[] =
    {
      &nettle_md2, &nettle_md4, &nettle_md5,
445
      OPENSSL(&nettle_openssl_md5)
446
      &nettle_sha1, OPENSSL(&nettle_openssl_sha1)
Niels Möller's avatar
Niels Möller committed
447 448
      &nettle_sha224, &nettle_sha256,
      &nettle_sha384, &nettle_sha512,
449 450 451
      NULL
    };

452
  const struct nettle_cipher *ciphers[] =
Niels Möller's avatar
Niels Möller committed
453
    {
454
      &nettle_aes128, &nettle_aes192, &nettle_aes256,
Niels Möller's avatar
Niels Möller committed
455 456 457 458 459
      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
460
      &nettle_camellia128, &nettle_camellia192, &nettle_camellia256,
461 462 463
      &nettle_cast128, OPENSSL(&nettle_openssl_cast128)
      &nettle_des, OPENSSL(&nettle_openssl_des)
      &nettle_des3,
464 465
      &nettle_serpent256,
      &nettle_twofish128, &nettle_twofish192, &nettle_twofish256,
466
      NULL
Niels Möller's avatar
Niels Möller committed
467
    };
468

469 470 471 472 473 474 475 476 477
  while ( (c = getopt(argc, argv, "f:")) != -1)
    switch (c)
      {
      case 'f':
	frequency = atof(optarg);
	if (frequency > 0.0)
	  break;

      case ':': case '?':
478
	fprintf(stderr, "Usage: nettle-benchmark [-f clock frequency] [alg]\n");
479 480 481 482 483 484
	return EXIT_FAILURE;

      default:
	abort();
    }

485 486
  alg = argv[optind];
  
487 488
  bench_sha1_compress();

489 490
  header();

491 492 493 494 495
  if (!alg || strstr ("memxor", alg))
    {
      time_memxor();
      printf("\n");
    }
496
  
497 498 499 500 501
  for (i = 0; hashes[i]; i++)
    {
      if (!alg || strstr(hashes[i]->name, alg))
	time_hash(hashes[i]);
    }
502
  for (i = 0; ciphers[i]; i++)
503 504 505
    if (!alg || strstr(ciphers[i]->name, alg))
      time_cipher(ciphers[i]);

506 507
  return 0;
}