nettle-benchmark.c 9.87 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
/* Process BENCH_BLOCK bytes at a time, for BENCH_INTERVAL seconds. */
58
#define BENCH_BLOCK 10240
59
#define BENCH_INTERVAL 0.25
Niels Möller's avatar
Niels Möller committed
60

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
/* 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

83
/* Returns second per function call */
84 85 86
static double
time_function(void (*f)(void *arg), void *arg)
{
87 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
  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 */
115 116
  clock_t before;
  clock_t after;
117 118
  clock_t done;
  
119
  before = clock();
120
  done = before + BENCH_INTERVAL * CLOCKS_PER_SEC;
121
  ncalls = 0;
122
  
123 124 125 126 127 128 129
  do 
    {
      f(arg);
      after = clock();
      ncalls++;
    }
  while (after < done);
130
  
131
  return ((double)(after - before)) / CLOCKS_PER_SEC / ncalls;
132
#endif /* !HAVE_CLOCK_GETTIME */
133 134
}

135 136 137
struct bench_hash_info
{
  void *ctx;
138
  nettle_hash_update_func *update;
139 140 141 142 143 144 145 146 147 148
  const uint8_t *data;
};

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

149 150 151
struct bench_cipher_info
{
  void *ctx;
152
  nettle_crypt_func *crypt;
153 154 155 156 157 158 159
  uint8_t *data;
};

static void
bench_cipher(void *arg)
{
  struct bench_cipher_info *info = arg;
160
  info->crypt(info->ctx, BENCH_BLOCK, info->data, info->data);
161 162 163 164 165
}

struct bench_cbc_info
{
  void *ctx;
166
  nettle_crypt_func *crypt;
167
 
168
  uint8_t *data;
169
  
170 171 172 173 174 175 176 177
  unsigned block_size;
  uint8_t *iv;
};

static void
bench_cbc_encrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
178 179 180
  cbc_encrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
181 182 183 184 185 186
}

static void
bench_cbc_decrypt(void *arg)
{
  struct bench_cbc_info *info = arg;
187 188 189
  cbc_decrypt(info->ctx, info->crypt,
	      info->block_size, info->iv,
	      BENCH_BLOCK, info->data, info->data);
190 191 192 193 194 195 196
}

/* 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
197
  for (i = j = 0; i<BENCH_BLOCK;  i++)
198 199 200 201 202 203 204 205
    {
      if (j*j < i)
	j++;
      data[i] = j;
    }
}

static void
Niels Möller's avatar
Niels Möller committed
206 207
init_key(unsigned length,
         uint8_t *key)
208
{
Niels Möller's avatar
Niels Möller committed
209 210 211
  unsigned i;
  for (i = 0; i<length; i++)
    key[i] = i;
212 213
}

214 215 216 217 218
static void
header(void)
{
  printf("%18s %11s Mbyte/s%s\n",
	 "Algorithm", "mode", 
Niels Möller's avatar
Niels Möller committed
219
	 frequency > 0.0 ? " cycles/byte cycles/block" : "");  
220 221
}

Niels Möller's avatar
Niels Möller committed
222
static void
Niels Möller's avatar
Niels Möller committed
223
display(const char *name, const char *mode, unsigned block_size,
224
	double time)
Niels Möller's avatar
Niels Möller committed
225
{
226
  printf("%18s %11s %7.2f",
Niels Möller's avatar
Niels Möller committed
227
	 name, mode,
228
	 BENCH_BLOCK / (time * 1048576.0));
229
  if (frequency > 0.0)
Niels Möller's avatar
Niels Möller committed
230
    {
231
      printf(" %11.2f", time * frequency / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
232
      if (block_size > 0)
233
	printf(" %12.2f", time * frequency * block_size / BENCH_BLOCK);
Niels Möller's avatar
Niels Möller committed
234
    }
235
  printf("\n");
Niels Möller's avatar
Niels Möller committed
236 237
}

238 239 240 241 242 243 244 245 246 247 248 249 250
static void *
xalloc(size_t size)
{
  void *p = malloc(size);
  if (!p)
    {
      fprintf(stderr, "Virtual memory exhausted.\n");
      abort();
    }

  return p;
}

251 252 253 254 255
static void
time_hash(const struct nettle_hash *hash)
{
  static uint8_t data[BENCH_BLOCK];
  struct bench_hash_info info;
256
  info.ctx = xalloc(hash->context_size); 
257 258 259 260 261 262
  info.update = hash->update;
  info.data = data;

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

Niels Möller's avatar
Niels Möller committed
263
  display(hash->name, "update", hash->block_size,
264
	  time_function(bench_hash, &info));
265 266

  free(info.ctx);
267 268
}

Niels Möller's avatar
Niels Möller committed
269
static void
270
time_cipher(const struct nettle_cipher *cipher)
Niels Möller's avatar
Niels Möller committed
271
{
272 273
  void *ctx = xalloc(cipher->context_size);
  uint8_t *key = xalloc(cipher->key_size);
Niels Möller's avatar
Niels Möller committed
274

275
  static uint8_t data[BENCH_BLOCK];
Niels Möller's avatar
Niels Möller committed
276 277 278 279

  printf("\n");
  
  init_data(data);
280 281

  {
Niels Möller's avatar
Niels Möller committed
282 283 284 285 286
    /* 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;
287
    
Niels Möller's avatar
Niels Möller committed
288
    init_key(cipher->key_size, key);
289
    cipher->set_encrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
290

Niels Möller's avatar
Niels Möller committed
291
    display(cipher->name, "ECB encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
292
	    time_function(bench_cipher, &info));
293
  }
Niels Möller's avatar
Niels Möller committed
294
  
295
  {
Niels Möller's avatar
Niels Möller committed
296 297 298 299
    struct bench_cipher_info info;
    info.ctx = ctx;
    info.crypt = cipher->decrypt;
    info.data = data;
300
    
Niels Möller's avatar
Niels Möller committed
301
    init_key(cipher->key_size, key);
302
    cipher->set_decrypt_key(ctx, cipher->key_size, key);
Niels Möller's avatar
Niels Möller committed
303

Niels Möller's avatar
Niels Möller committed
304
    display(cipher->name, "ECB decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
305
	    time_function(bench_cipher, &info));
306 307
  }

Niels Möller's avatar
Niels Möller committed
308 309
  /* 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
310
    {
311
      uint8_t *iv = xalloc(cipher->block_size);
Niels Möller's avatar
Niels Möller committed
312 313 314
      
      /* Do CBC mode */
      {
Niels Möller's avatar
Niels Möller committed
315 316 317 318 319 320
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->encrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
321
    
Niels Möller's avatar
Niels Möller committed
322
        memset(iv, 0, sizeof(iv));
323
    
324
        cipher->set_encrypt_key(ctx, cipher->key_size, key);
325

Niels Möller's avatar
Niels Möller committed
326
	display(cipher->name, "CBC encrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
327
		time_function(bench_cbc_encrypt, &info));
Niels Möller's avatar
Niels Möller committed
328
      }
329

Niels Möller's avatar
Niels Möller committed
330
      {
Niels Möller's avatar
Niels Möller committed
331 332 333 334 335 336
        struct bench_cbc_info info;
	info.ctx = ctx;
	info.crypt = cipher->decrypt;
	info.data = data;
	info.block_size = cipher->block_size;
	info.iv = iv;
337
    
Niels Möller's avatar
Niels Möller committed
338
        memset(iv, 0, sizeof(iv));
339

340
        cipher->set_decrypt_key(ctx, cipher->key_size, key);
341

Niels Möller's avatar
Niels Möller committed
342
	display(cipher->name, "CBC decrypt", cipher->block_size,
Niels Möller's avatar
Niels Möller committed
343
		time_function(bench_cbc_decrypt, &info));
Niels Möller's avatar
Niels Möller committed
344
      }
345
      free(iv);
Niels Möller's avatar
Niels Möller committed
346
    }
347 348
  free(ctx);
  free(key);
Niels Möller's avatar
Niels Möller committed
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 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
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
399
#if WITH_OPENSSL
400 401 402 403
# define OPENSSL(x) x,
#else
# define OPENSSL(x)
#endif
Niels Möller's avatar
Niels Möller committed
404 405

int
406
main(int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
407 408
{
  unsigned i;
409
  int c;
410 411 412 413

  const struct nettle_hash *hashes[] =
    {
      &nettle_md2, &nettle_md4, &nettle_md5,
414
      OPENSSL(&nettle_openssl_md5)
415
      &nettle_sha1, OPENSSL(&nettle_openssl_sha1)
Niels Möller's avatar
Niels Möller committed
416 417
      &nettle_sha224, &nettle_sha256,
      &nettle_sha384, &nettle_sha512,
418 419 420
      NULL
    };

421
  const struct nettle_cipher *ciphers[] =
Niels Möller's avatar
Niels Möller committed
422
    {
423
      &nettle_aes128, &nettle_aes192, &nettle_aes256,
Niels Möller's avatar
Niels Möller committed
424 425 426 427 428
      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
429
      &nettle_camellia128, &nettle_camellia192, &nettle_camellia256,
430 431 432
      &nettle_cast128, OPENSSL(&nettle_openssl_cast128)
      &nettle_des, OPENSSL(&nettle_openssl_des)
      &nettle_des3,
433 434
      &nettle_serpent256,
      &nettle_twofish128, &nettle_twofish192, &nettle_twofish256,
435
      NULL
Niels Möller's avatar
Niels Möller committed
436
    };
437

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
  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();
    }

454 455
  bench_sha1_compress();

456 457
  header();

458 459 460
  for (i = 0; hashes[i]; i++)
    time_hash(hashes[i]);
  
461
  for (i = 0; ciphers[i]; i++)
462
    time_cipher(ciphers[i]);
Niels Möller's avatar
Niels Möller committed
463
  
464 465
  return 0;
}