progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / progs / perftest.c
index f064c2a..7c4a133 100644 (file)
 #include <sys/time.h>
 #include <unistd.h>
 
+#ifdef HAVE_LINUX_PERF_EVENT_H
+#  include <sys/syscall.h>
+#  include <linux/perf_event.h>
+#endif
+
 #include <mLib/alloc.h>
+#include <mLib/bits.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -70,6 +77,7 @@
 #include "ed448.h"
 
 #include "cc.h"
+#include "gaead.h"
 #include "gcipher.h"
 #include "ghash.h"
 #include "gmac.h"
 
 typedef struct opts {
   const char *name;                    /* Pre-configured named thing */
+  const char *opwhat;                  /* What to call operations */
   unsigned fbits;                      /* Field size bits */
   unsigned gbits;                      /* Group size bits */
   unsigned n;                          /* Number of factors */
   unsigned i;                          /* Number of intervals (or zero) */
+  unsigned k;                          /* Main loop batch size */
+  unsigned long sc;                    /* Scale factor */
   double t;                            /* Time for each interval (secs) */
   mp *e;                               /* Public exponent */
   unsigned f;                          /* Flags */
@@ -495,7 +506,9 @@ static void *ksched_init(opts *o)
     die(1, "must specify encryption scheme name");
   if ((c->c = gcipher_byname(o->name)) == 0)
     die(1, "encryption scheme `%s' not known", o->name);
-  c->ksz = keysz(o->gbits/8, c->c->keysz);
+  c->ksz = keysz(o->fbits/8, c->c->keysz);
+  if (o->fbits%8 || (o->fbits && c->ksz != o->fbits/8))
+    die(1, "bad key size %u for %s", o->fbits, o->name);
   c->k = xmalloc(c->ksz);
   rand_get(RAND_GLOBAL, c->k, c->ksz);
   return (c);
@@ -525,13 +538,16 @@ static void *enc_init(opts *o)
     die(1, "must specify encryption scheme name");
   if ((cc = gcipher_byname(o->name)) == 0)
     die(1, "encryption scheme `%s' not known", o->name);
-  ksz = keysz(0, cc->keysz);
+  ksz = keysz(o->fbits/8, cc->keysz);
+  if (o->fbits%8 || (o->fbits && ksz != o->fbits/8))
+    die(1, "bad key size %u for %s", o->fbits, o->name);
   k = xmalloc(ksz);
   rand_get(RAND_GLOBAL, k, ksz);
   c->c = GC_INIT(cc, k, ksz);
   xfree(k);
   c->sz = o->gbits ? o->gbits : 65536;
   c->n = o->n ? o->n : 16;
+  o->opwhat = "byte"; o->sc = c->n*c->sz;
   c->m = xmalloc(c->sz);
   return (c);
 }
@@ -544,6 +560,105 @@ static void enc_run(void *cc)
     GC_ENCRYPT(c->c, c->m, c->m, c->sz);
 }
 
+/* --- Authenticated encryption --- */
+
+typedef struct aeadsetup_ctx {
+  const gcaead *aec;
+  octet *k; size_t ksz;
+  octet *n; size_t nsz;
+  size_t tsz;
+} aeadsetup_ctx;
+
+static void *aeadsetup_init(opts *o)
+{
+  aeadsetup_ctx *c = CREATE(aeadsetup_ctx);
+  if (!o->name)
+    die(1, "must specify encryption scheme name");
+  if ((c->aec = gaead_byname(o->name)) == 0)
+    die(1, "aead scheme `%s' not known", o->name);
+  c->ksz = keysz(o->fbits/8, c->aec->keysz);
+  c->nsz = keysz_pad(o->gbits/8, c->aec->noncesz);
+  c->tsz = keysz(0, c->aec->tagsz);
+  if (o->fbits%8 || (o->fbits && c->ksz != o->fbits/8))
+    die(1, "bad key size %u for %s", o->fbits, o->name);
+  if (o->gbits%8 || (o->gbits && c->nsz != o->gbits/8))
+    die(1, "bad nonce size %u for %s", o->gbits, o->name);
+  c->k = xmalloc(c->ksz); rand_get(RAND_GLOBAL, c->k, c->ksz);
+  c->n = xmalloc(c->nsz); rand_get(RAND_GLOBAL, c->n, c->nsz);
+  return (c);
+}
+
+static void aeadsetup_run(void *cc)
+{
+  aeadsetup_ctx *c = cc;
+  gaead_key *k = GAEAD_KEY(c->aec, c->k, c->ksz);
+  gaead_enc *e = GAEAD_ENC(k, c->n, c->nsz, 0, 0, c->tsz);
+  GAEAD_DESTROY(e); GAEAD_DESTROY(k);
+}
+
+typedef struct aeadenc_ctx {
+  gaead_enc *enc;
+  octet *n; size_t nsz;
+  octet *p, *q; size_t sz; size_t nn;
+  size_t tsz;
+} aeadenc_ctx;
+
+static void *aeadenc_init(opts *o)
+{
+  aeadenc_ctx *c = CREATE(aeadenc_ctx);
+  const gcaead *aec;
+  gaead_key *key;
+  octet *k; size_t ksz;
+
+  if (!o->name)
+    die(1, "must specify encryption scheme name");
+  if ((aec = gaead_byname(o->name)) == 0)
+    die(1, "aead scheme `%s' not known", o->name);
+  c->sz = o->gbits ? o->gbits : 65536;
+  c->nn = o->n ? o->n : 16;
+  ksz = keysz(o->fbits/8, aec->keysz);
+  c->nsz = keysz(0, aec->noncesz);
+  c->tsz = keysz(0, aec->tagsz);
+  if (o->fbits%8 || (o->fbits && ksz != o->fbits/8))
+    die(1, "bad key size %u for %s", o->fbits, o->name);
+
+  k = xmalloc(ksz); rand_get(RAND_GLOBAL, k, ksz);
+  c->n = xmalloc(c->nsz); rand_get(RAND_GLOBAL, c->n, c->nsz);
+  c->p = xmalloc(c->sz); c->q = xmalloc(c->sz + aec->bufsz);
+
+  key = GAEAD_KEY(aec, k, ksz);
+  c->enc = GAEAD_ENC(key, c->n, c->nsz, 0, 0, c->tsz);
+  GAEAD_DESTROY(key); xfree(k);
+
+  o->opwhat = "byte"; o->sc = c->nn*c->sz;
+  return (c);
+}
+
+static void aeadaad_run(void *cc)
+{
+  aeadenc_ctx *c = cc;
+  gaead_aad *a;
+  size_t i;
+
+  GAEAD_REINIT(c->enc, c->n, c->nsz, c->nn*c->sz, 0, c->tsz);
+  a = GAEAD_AAD(c->enc);
+  for (i = 0; i < c->nn; i++) GAEAD_HASH(a, c->p, c->sz);
+  GAEAD_DESTROY(a);
+}
+
+static void aeadenc_run(void *cc)
+{
+  aeadenc_ctx *c = cc;
+  buf b;
+  size_t i;
+
+  GAEAD_REINIT(c->enc, c->n, c->nsz, 0, c->nn*c->sz, c->tsz);
+  for (i = 0; i < c->nn; i++) {
+    buf_init(&b, c->q, c->sz + c->enc->ops->c->bufsz);
+    GAEAD_ENCRYPT(c->enc, c->p, c->sz, &b);
+  }
+}
+
 /* --- Hashing --- */
 
 typedef struct hash_ctx {
@@ -562,6 +677,7 @@ static void *hash_init(opts *o)
     die(1, "hash function `%s' not known", o->name);
   c->sz = o->gbits ? o->gbits : 65536;
   c->n = o->n ? o->n : 16;
+  o->opwhat = "byte"; o->sc = c->n*c->sz;
   c->m = xmalloc(c->sz);
   return (c);
 }
@@ -596,6 +712,7 @@ static void *poly1305_jobinit(opts *o)
   rand_get(RAND_GLOBAL, c->s, sizeof(c->s));
   c->sz = o->gbits ? o->gbits : 65536;
   c->n = o->n ? o->n : 16;
+  o->opwhat = "byte"; o->sc = c->n*c->sz;
   c->m = xmalloc(c->sz);
   return (c);
 }
@@ -639,11 +756,81 @@ static const jobops jobtab[] = {
   { "ed448-vrf",               ed448_vrfinit,          ed448_vrfrun },
   { "ksched",                  ksched_init,            ksched_run },
   { "enc",                     enc_init,               enc_run },
+  { "aead-setup",              aeadsetup_init,         aeadsetup_run },
+  { "aead-aad",                        aeadenc_init,           aeadaad_run },
+  { "aead-enc",                        aeadenc_init,           aeadenc_run },
   { "hash",                    hash_init,              hash_run },
   { "poly1305",                        poly1305_jobinit,       poly1305_jobrun },
   { 0,                         0,                      0 }
 };
 
+/*----- Cycle counting ----------------------------------------------------*/
+
+typedef kludge64 cycles;
+static int cyclecount_active_p = 0;
+
+#if defined(__GNUC__) && (CPUFAM_X86 || CPUFAM_AMD64)
+
+static void init_cyclecount(void) { cyclecount_active_p = 1; }
+
+static cycles cyclecount(void)
+{
+  uint32 lo, hi;
+  kludge64 cy;
+
+  __asm__("rdtsc" : "=a"(lo), "=d"(hi));
+  SET64(cy, hi, lo);
+  return cy;
+}
+
+#elif defined(HAVE_LINUX_PERF_EVENT_H) && defined(HAVE_UINT64)
+
+static int perf_fd = -1;
+
+static void init_cyclecount(void)
+{
+  struct perf_event_attr attr = { 0 };
+
+  attr.type = PERF_TYPE_HARDWARE;
+  attr.size = sizeof(attr);
+  attr.config = PERF_COUNT_HW_CPU_CYCLES;
+  attr.disabled = 0;
+  attr.exclude_kernel = 1;
+  attr.exclude_hv = 1;
+
+  if ((perf_fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0)) < 0)
+    moan("failed to open perf event: %s", strerror(errno));
+  else
+    cyclecount_active_p = 1;
+}
+
+static cycles cyclecount(void)
+{
+  kludge64 cy;
+  ssize_t n;
+
+  if (!cyclecount_active_p)
+    goto fail;
+  else if ((n = read(perf_fd, &cy.i, sizeof(cy.i))) != sizeof(cy.i)) {
+    if (n < 0) moan("error reading perf event: %s", strerror(errno));
+    else moan("unexpected short read from perf event");
+    cyclecount_active_p = 0; close(perf_fd); perf_fd = -1;
+    goto fail;
+  }
+end:
+  return (cy);
+fail:
+  SET64(cy, 0, 0);
+  goto end;
+}
+
+#else
+
+static void init_cyclecount(void) { cyclecount_active_p = 0; }
+static cycles cyclecount(void) { kludge64 cy; SET64(cy, 0, 0); return (cy); }
+
+#endif
+
 /*----- Main code ---------------------------------------------------------*/
 
 void version(FILE *fp)
@@ -672,12 +859,15 @@ Options:\n\
 -l, --list [ITEM...]   List all the various names of things.\n\
 \n\
 -C, --name=NAME                Select curve/DH-group/enc/hash name.\n\
--b, --field-bits       Field size for g-prime and rsa.\n\
+-b, --field-bits       Field size for g-prime and rsa;\n\
+                         key bits for ksched, enc, aead-setup, aead-enc.\n\
 -q, --no-check         Don't check field/group for validity.\n\
--B, --group-bits       Group size for g-prime; key size for ksched;\n\
-                         data size for enc and hash.\n\
--n, --factors=COUNT    Number of factors for {exp,mul}-sim.\n\
+-B, --group-bits       Group size for g-prime; nonce bits for aead-setup;\n\
+                         data size for enc, aead-aad, aead-enc, and hash.\n\
+-n, --factors=COUNT    Number of factors for {exp,mul}-sim;\n\
+                         inner iters for enc, aead-aad, aead-enc, hash.\n\
 -i, --intervals=COUNT  Number of intervals to run for.  [0; forever]\n\
+-k, --batch=COUNT      Number of operations to batch between timer checks.\n\
 -t, --time=TIME                Length of an interval in seconds.  [1]\n\
 ");
 }
@@ -693,6 +883,8 @@ Options:\n\
      ptab[i].name, ptab[i].name)                                       \
   LI("Encryption algorithms", cipher,                                  \
      gciphertab[i], gciphertab[i]->name)                               \
+  LI("Authenticated encryption schemes", aead,                         \
+     gaeadtab[i], gaeadtab[i]->name)                                   \
   LI("Hash functions", hash,                                           \
      ghashtab[i], ghashtab[i]->name)
 
@@ -734,15 +926,16 @@ int main(int argc, char *argv[])
   opts o = { 0 };
   const jobops *j;
   struct timeval tv_next, tv_now;
-  double t, ttot;
-  unsigned n;
+  double t, ttot, cy, cytot;
+  unsigned n, k;
   unsigned long ii;
-  clock_t c_start, c_stop;
+  clock_t c0, c1;
+  kludge64 cy0, cy1, cydiff;
   double itot;
   void *p;
 
   ego(argv[0]);
-  o.t = 1;
+  o.t = 1; o.k = 1; o.sc = 1; o.opwhat = "op";
   for (;;) {
     static const struct option opts[] = {
       { "help",                0,              0,      'h' },
@@ -754,13 +947,14 @@ int main(int argc, char *argv[])
       { "group-bits",  OPTF_ARGREQ,    0,      'B' },
       { "factors",     OPTF_ARGREQ,    0,      'n' },
       { "intervals",   OPTF_ARGREQ,    0,      'i' },
+      { "batch",       OPTF_ARGREQ,    0,      'k' },
       { "public-exponent", OPTF_ARGREQ, 0,     'e' },
       { "time",                OPTF_ARGREQ,    0,      't' },
       { "no-check",    0,              0,      'q' },
       { 0,             0,              0,      0 }
     };
 
-    i = mdwopt(argc, argv, "hvulC:b:B:n:i:e:t:q", opts, 0, 0, 0);
+    i = mdwopt(argc, argv, "hvulC:b:B:n:i:k:e:t:q", opts, 0, 0, 0);
     if (i < 0) break;
     switch (i) {
       case 'h': help(stdout); exit(0);
@@ -778,6 +972,7 @@ int main(int argc, char *argv[])
        break;
       case 'i': o.i = uarg("interval count", optarg); break;
       case 't': o.t = farg("interval length", optarg); break;
+      case 'k': o.k = uarg("batch size", optarg); break;
       case 'q': o.f |= OF_NOCHECK; break;
       default: usage(stderr); exit(1);
     }
@@ -785,28 +980,34 @@ int main(int argc, char *argv[])
   if (optind + 1 != argc) { usage(stderr); exit(1); }
 
   for (j = jobtab; j->name; j++)
-    if (strcmp(j->name, argv[optind]) == 0) break;
+    if (STRCMP(j->name, ==, argv[optind])) break;
   if (!j->name) die(1, "unknown job type `%s'", argv[optind]);
   p = j->init(&o);
 
   n = 0;
-  ttot = itot =         0;
+  ttot = itot = 0; cytot = 0; init_cyclecount();
   gettimeofday(&tv_now, 0);
   do {
     tv_addl(&tv_next, &tv_now, o.t, fmod(o.t * MILLION, MILLION));
     ii = 0;
-    c_start = clock();
+    c0 = clock(); cy0 = cyclecount();
     do {
-      j->run(p);
-      ii++;
+      for (k = 0; k < o.k; k++) { j->run(p); }
+      ii += k;
       gettimeofday(&tv_now, 0);
     } while (TV_CMP(&tv_now, <, &tv_next));
-    c_stop = clock();
-    t = (double)(c_stop - c_start)/CLOCKS_PER_SEC;
-    itot += ii;
-    ttot += t;
-    printf("%5u: did = %5lu; /sec = %5f; avg /sec = %5f\n",
+    cy1 = cyclecount(); c1 = clock();
+    t = (double)(c1 - c0)/CLOCKS_PER_SEC;
+    itot += ii; ttot += t;
+    printf("%5u: did = %5lu; /sec = %5f; avg /sec = %5f",
           n, ii, ii/t, itot/ttot);
+    if (cyclecount_active_p) {
+      SUB64(cydiff, cy1, cy0); cy = LO64(cydiff) + ldexp(HI64(cydiff), 32);
+      cytot += cy;
+      printf(" (cy/%s = %3f; avg cy/%s = %3f)",
+            o.opwhat, cy/ii/o.sc, o.opwhat, cytot/itot/o.sc);
+    }
+    putchar('\n');
     fflush(stdout);
     n++;
   } while (!o.i || n < o.i);