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 }