keccak-fast.c

Minimal SIMD keccak implementation
git clone git://git.finwo.net/lib/keccak-fast.c
Log | Files | Refs | README | LICENSE

bench.c (6390B)


      1 #if defined(__linux__)
      2 #define _GNU_SOURCE
      3 #include <sched.h>
      4 #endif
      5 #include <stdint.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <time.h>
     10 
     11 #include "keccak-fast.h"
     12 #include "keccak-fast-internal.h"
     13 
     14 int kct_sha3_256(uint8_t *, size_t, const uint8_t *, size_t);
     15 int kct_shake256(uint8_t *, size_t, const uint8_t *, size_t);
     16 
     17 #ifndef BENCH_FLAGS
     18 #define BENCH_FLAGS ""
     19 #endif
     20 
     21 typedef int (*kf_fn)(uint8_t *, size_t, const uint8_t *, size_t);
     22 typedef int (*kf_batch_fn)(size_t, const uint8_t *, size_t, uint8_t *, size_t);
     23 
     24 static volatile uint64_t g_sink;
     25 
     26 static uint64_t now_ns(void) {
     27   struct timespec ts;
     28   clock_gettime(CLOCK_MONOTONIC, &ts);
     29   return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
     30 }
     31 
     32 static int pin_cpu(int cpu) {
     33 #if defined(__linux__)
     34   cpu_set_t set;
     35   CPU_ZERO(&set);
     36   CPU_SET(cpu, &set);
     37   return sched_setaffinity(0, sizeof set, &set);
     38 #else
     39   (void)cpu;
     40   return -1;
     41 #endif
     42 }
     43 
     44 static double time_once(kf_fn fn, uint8_t *out, size_t outlen,
     45                         const uint8_t *in, size_t inlen, size_t iters) {
     46   uint64_t t0 = now_ns();
     47   for (size_t i = 0; i < iters; i++) {
     48     fn(out, outlen, in, inlen);
     49     g_sink += out[0];
     50   }
     51   return (double)(now_ns() - t0) / (double)iters;
     52 }
     53 
     54 static size_t pick_iters(kf_fn fn, uint8_t *out, size_t outlen,
     55                          const uint8_t *in, size_t inlen, uint64_t target_ns) {
     56   double per = time_once(fn, out, outlen, in, inlen, 1000);
     57   if (per <= 0.0) per = 1.0;
     58   size_t iters = (size_t)((double)target_ns / per);
     59   return iters < 100 ? 100 : iters;
     60 }
     61 
     62 typedef struct {
     63   const char *label;
     64   kf_fn       fn;
     65 } oneshot_t;
     66 
     67 static double time_batch(kf_batch_fn fn, size_t count, const uint8_t *in,
     68                          size_t inlen, uint8_t *out, size_t outlen,
     69                          size_t iters) {
     70   uint64_t t0 = now_ns();
     71   for (size_t i = 0; i < iters; i++) {
     72     fn(count, in, inlen, out, outlen);
     73     g_sink += out[0];
     74   }
     75   return (double)(now_ns() - t0) / ((double)iters * (double)count);
     76 }
     77 
     78 typedef struct {
     79   const char      *label;
     80   kf_batch_perm_fn perm;
     81   kf_batch_perm_fn perm12;
     82   int              lanes;
     83 } batch_t;
     84 
     85 static void case_all(const char *title, const oneshot_t *os, int nos,
     86                      const batch_t *bs, int nbs, kf_fn donor,
     87                      kf_batch_fn batch_fn, size_t count, size_t inlen,
     88                      size_t outlen, uint8_t *in, uint8_t *out, int reps,
     89                      uint64_t target_ns) {
     90   double bd = -1.0, bos[4], bbs[4];
     91   kf_fn  iters_fn = donor ? donor : os[0].fn;
     92   size_t iters = pick_iters(iters_fn, out, outlen, in, inlen, target_ns);
     93   for (int i = 0; i < nos; i++) bos[i] = -1.0;
     94   for (int i = 0; i < nbs; i++) bbs[i] = -1.0;
     95 
     96   size_t biters = 0;
     97   if (nbs > 0) {
     98     kf_batch_perm   = bs[0].perm;
     99     kf_batch_perm12 = bs[0].perm12;
    100     kf_batch_lanes  = bs[0].lanes;
    101     double per = time_batch(batch_fn, count, in, inlen, out, outlen, 200);
    102     if (per <= 0.0) per = 1.0;
    103     biters = (size_t)((double)target_ns / (per * (double)count));
    104     if (biters < 10) biters = 10;
    105   }
    106 
    107   for (int r = 0; r < reps; r++) {
    108     if (donor != NULL) {
    109       double d = time_once(donor, out, outlen, in, inlen, iters);
    110       if (bd < 0.0 || d < bd) bd = d;
    111     }
    112     for (int i = 0; i < nos; i++) {
    113       double v = time_once(os[i].fn, out, outlen, in, inlen, iters);
    114       if (bos[i] < 0.0 || v < bos[i]) bos[i] = v;
    115     }
    116     for (int i = 0; i < nbs; i++) {
    117       kf_batch_perm   = bs[i].perm;
    118       kf_batch_perm12 = bs[i].perm12;
    119       kf_batch_lanes  = bs[i].lanes;
    120       double v = time_batch(batch_fn, count, in, inlen, out, outlen, biters);
    121       if (bbs[i] < 0.0 || v < bbs[i]) bbs[i] = v;
    122     }
    123   }
    124 
    125   printf("%s\n", title);
    126   if (donor != NULL) {
    127     printf("  %-10s %9.2f ns/hash %9.3f Mh/s\n", "donor", bd, 1000.0 / bd);
    128   }
    129   for (int i = 0; i < nos; i++)
    130     printf("  %-10s %9.2f ns/hash %9.3f Mh/s\n", os[i].label, bos[i],
    131            1000.0 / bos[i]);
    132   for (int i = 0; i < nbs; i++)
    133     printf("  %-10s %9.2f ns/hash %9.3f Mh/s\n", bs[i].label, bbs[i],
    134            1000.0 / bbs[i]);
    135   printf("\n");
    136 }
    137 
    138 int main(void) {
    139   const int      reps      = 9;
    140   const uint64_t target_ns = 25ULL * 1000000ULL;
    141   static uint8_t in[64 * 1088];
    142   static uint8_t out[64 * 1088];
    143   int            cpu = 0;
    144   const char    *cs  = getenv("BENCH_CPU");
    145 
    146   for (size_t i = 0; i < sizeof in; i++) in[i] = (uint8_t)(i * 31 + 7);
    147   if (cs) cpu = atoi(cs);
    148 
    149   printf("keccak-fast backend comparison\n");
    150   printf("build: %s\n", BENCH_FLAGS);
    151   if (pin_cpu(cpu) != 0) {
    152     printf("# warning: could not pin to cpu %d, results will be noisier\n", cpu);
    153   } else {
    154     printf("pinned to cpu %d\n", cpu);
    155   }
    156   printf("best of %d interleaved reps, about %llu ms per rep\n\n", reps,
    157          (unsigned long long)(target_ns / 1000000ULL));
    158 
    159   const oneshot_t sha3[] = {
    160       {"scalar", kf_scalar_sha3_256},
    161       {kf_backend_name(), kf_sha3_256},
    162   };
    163   const oneshot_t shake[] = {
    164       {"scalar", kf_scalar_shake256},
    165       {kf_backend_name(), kf_shake256},
    166   };
    167   const oneshot_t ts256[] = {
    168       {"scalar", kf_scalar_turboshake256},
    169       {kf_backend_name(), kf_turboshake256},
    170   };
    171 
    172   batch_t batch[4];
    173   int     nb = 0;
    174 #if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
    175     !defined(KECCAK_FAST_NO_AVX2)
    176   batch[nb++] = (batch_t){"avx2 x4", kf_avx2_batch, kf_avx2_batch12, 4};
    177 #endif
    178 #if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
    179     !defined(KECCAK_FAST_NO_AVX512)
    180   batch[nb++] = (batch_t){"avx512 x8", kf_avx512_batch, kf_avx512_batch12, 8};
    181 #endif
    182 
    183   case_all("SHA3-256 32->32, per hash", sha3, 2, batch, nb, kct_sha3_256,
    184            kf_sha3_256_batch, 64, 32, 32, in, out, reps, target_ns);
    185   case_all("SHA3-256 64->32, per hash", sha3, 2, batch, nb, kct_sha3_256,
    186            kf_sha3_256_batch, 64, 64, 32, in, out, reps, target_ns);
    187   case_all("SHAKE256 32->32, per hash", shake, 2, batch, nb, kct_shake256,
    188            kf_shake256_batch, 64, 32, 32, in, out, reps, target_ns);
    189   case_all("SHAKE256 32->1088, per hash", shake, 2, batch, nb, kct_shake256,
    190            kf_shake256_batch, 64, 32, 1088, in, out, reps, target_ns);
    191   case_all("TurboSHAKE256 32->32, per hash", ts256, 2, batch, nb, NULL,
    192            kf_turboshake256_batch, 64, 32, 32, in, out, reps, target_ns);
    193 
    194   printf("sink %llu\n", (unsigned long long)g_sink);
    195   return 0;
    196 }