commit 84898a129c15d87587135e8c245742617b0d0e7b
parent 51be4c74a70bfdcfa108768dde7ff034d782d387
Author: finwo <finwo@pm.me>
Date: Sat, 10 Oct 2026 17:28:25 +0200
Bench all backends
Diffstat:
6 files changed, 100 insertions(+), 66 deletions(-)
diff --git a/README.md b/README.md
@@ -54,7 +54,7 @@ also gets `rorx` for the rotates. About 20% faster than the donor.
```
make test # KAT and batch differential (TAP)
-make bench # vendored donor, pinned core, best-of-nine ns/hash
+make bench # per-hash table: scalar, scalar+bmi, avx2, avx512 (+ donor)
```
`bench/` fetches the donor via `dep install`. `OPT=-O2|-O3` (arch flags
diff --git a/bench/bench.c b/bench/bench.c
@@ -7,6 +7,7 @@
#include <time.h>
#include "keccak-fast.h"
+#include "keccak-fast-internal.h"
int kct_sha3_256(uint8_t *, size_t, const uint8_t *, size_t);
int kct_shake256(uint8_t *, size_t, const uint8_t *, size_t);
@@ -51,24 +52,10 @@ static size_t pick_iters(kf_fn fn, uint8_t *out, size_t outlen,
return iters < 100 ? 100 : iters;
}
-static void run_pair(const char *name, kf_fn donor, kf_fn fast, size_t inlen,
- size_t outlen, uint8_t *in, uint8_t *out, int reps,
- uint64_t target_ns) {
- size_t iters = pick_iters(donor, out, outlen, in, inlen, target_ns);
- double bt = -1.0;
- double bf = -1.0;
- for (int r = 0; r < reps; r++) {
- double t = time_once(donor, out, outlen, in, inlen, iters);
- double f = time_once(fast, out, outlen, in, inlen, iters);
- if (bt < 0.0 || t < bt) bt = t;
- if (bf < 0.0 || f < bf) bf = f;
- }
- printf("%s\n", name);
- printf(" %-8s %9.2f ns/hash %9.3f Mh/s\n", "donor", bt, 1000.0 / bt);
- printf(" %-8s %9.2f ns/hash %9.3f Mh/s\n", kf_backend_name(), bf,
- 1000.0 / bf);
- printf(" %-8s %9.3fx\n\n", "fast/do", bf / bt);
-}
+typedef struct {
+ const char *label;
+ kf_fn fn;
+} oneshot_t;
static double time_batch(kf_batch_fn fn, size_t count, const uint8_t *in,
size_t inlen, uint8_t *out, size_t outlen,
@@ -81,36 +68,70 @@ static double time_batch(kf_batch_fn fn, size_t count, const uint8_t *in,
return (double)(now_ns() - t0) / ((double)iters * (double)count);
}
-static void case_batch(const char *name, kf_batch_fn fn, size_t count,
- size_t inlen, size_t outlen, uint8_t *in, uint8_t *out,
- int reps, uint64_t target_ns) {
- double per = time_batch(fn, count, in, inlen, out, outlen, 200);
- if (per <= 0.0) per = 1.0;
- size_t iters = (size_t)((double)target_ns / (per * (double)count));
- if (iters < 10) iters = 10;
+typedef struct {
+ const char *label;
+ kf_batch_perm_fn perm;
+ int lanes;
+} batch_t;
+
+static void case_all(const char *title, const oneshot_t *os, int nos,
+ const batch_t *bs, int nbs, kf_fn donor,
+ kf_batch_fn batch_fn, size_t count, size_t inlen,
+ size_t outlen, uint8_t *in, uint8_t *out, int reps,
+ uint64_t target_ns) {
+ double bd = -1.0, bos[4], bbs[4];
+ size_t iters = pick_iters(donor, out, outlen, in, inlen, target_ns);
+ for (int i = 0; i < nos; i++) bos[i] = -1.0;
+ for (int i = 0; i < nbs; i++) bbs[i] = -1.0;
+
+ size_t biters = 0;
+ if (nbs > 0) {
+ kf_batch_perm = bs[0].perm;
+ kf_batch_lanes = bs[0].lanes;
+ double per = time_batch(batch_fn, count, in, inlen, out, outlen, 200);
+ if (per <= 0.0) per = 1.0;
+ biters = (size_t)((double)target_ns / (per * (double)count));
+ if (biters < 10) biters = 10;
+ }
- double best = -1.0;
for (int r = 0; r < reps; r++) {
- double v = time_batch(fn, count, in, inlen, out, outlen, iters);
- if (best < 0.0 || v < best) best = v;
+ double d = time_once(donor, out, outlen, in, inlen, iters);
+ if (bd < 0.0 || d < bd) bd = d;
+ for (int i = 0; i < nos; i++) {
+ double v = time_once(os[i].fn, out, outlen, in, inlen, iters);
+ if (bos[i] < 0.0 || v < bos[i]) bos[i] = v;
+ }
+ for (int i = 0; i < nbs; i++) {
+ kf_batch_perm = bs[i].perm;
+ kf_batch_lanes = bs[i].lanes;
+ double v = time_batch(batch_fn, count, in, inlen, out, outlen, biters);
+ if (bbs[i] < 0.0 || v < bbs[i]) bbs[i] = v;
+ }
}
- char label[64];
- snprintf(label, sizeof label, "%s count %zu", name, count);
- printf("%-24s %9.2f ns/hash %9.3f Mh/s\n", label, best, 1000.0 / best);
+
+ printf("%s\n", title);
+ printf(" %-10s %9.2f ns/hash %9.3f Mh/s\n", "donor", bd, 1000.0 / bd);
+ for (int i = 0; i < nos; i++)
+ printf(" %-10s %9.2f ns/hash %9.3f Mh/s\n", os[i].label, bos[i],
+ 1000.0 / bos[i]);
+ for (int i = 0; i < nbs; i++)
+ printf(" %-10s %9.2f ns/hash %9.3f Mh/s\n", bs[i].label, bbs[i],
+ 1000.0 / bbs[i]);
+ printf("\n");
}
int main(void) {
const int reps = 9;
const uint64_t target_ns = 25ULL * 1000000ULL;
- uint8_t in[256];
- static uint8_t out[4096];
+ static uint8_t in[64 * 1088];
+ static uint8_t out[64 * 1088];
int cpu = 0;
const char *cs = getenv("BENCH_CPU");
for (size_t i = 0; i < sizeof in; i++) in[i] = (uint8_t)(i * 31 + 7);
if (cs) cpu = atoi(cs);
- printf("keccak-fast vs coruus/keccak-tiny\n");
+ printf("keccak-fast backend comparison\n");
printf("build: %s\n", BENCH_FLAGS);
if (pin_cpu(cpu) != 0) {
printf("# warning: could not pin to cpu %d, results will be noisier\n", cpu);
@@ -120,34 +141,34 @@ int main(void) {
printf("best of %d interleaved reps, about %llu ms per rep\n\n", reps,
(unsigned long long)(target_ns / 1000000ULL));
- run_pair("SHA3-256 32->32", kct_sha3_256, kf_sha3_256, 32, 32, in, out, reps,
- target_ns);
- run_pair("SHA3-256 64->32", kct_sha3_256, kf_sha3_256, 64, 32, in, out, reps,
- target_ns);
- run_pair("SHAKE256 32->32", kct_shake256, kf_shake256, 32, 32, in, out, reps,
- target_ns);
- run_pair("SHAKE256 32->1088", kct_shake256, kf_shake256, 32, 1088, in, out, reps,
- target_ns);
-
- static uint8_t bin[256 * 64];
- static uint8_t bout[256 * 64];
- for (size_t i = 0; i < sizeof bin; i++) bin[i] = (uint8_t)(i * 17 + 3);
-
- printf("batch SHAKE256 32->32, per hash (%s)\n", kf_batch_name());
- case_batch(kf_batch_name(), kf_shake256_batch, 8, 32, 32, bin, bout, reps,
- target_ns);
- case_batch(kf_batch_name(), kf_shake256_batch, 64, 32, 32, bin, bout, reps,
- target_ns);
- case_batch(kf_batch_name(), kf_shake256_batch, 256, 32, 32, bin, bout, reps,
- target_ns);
-
- printf("batch SHA3-256 32->32, per hash (%s)\n", kf_batch_name());
- case_batch(kf_batch_name(), kf_sha3_256_batch, 8, 32, 32, bin, bout, reps,
- target_ns);
- case_batch(kf_batch_name(), kf_sha3_256_batch, 64, 32, 32, bin, bout, reps,
- target_ns);
- case_batch(kf_batch_name(), kf_sha3_256_batch, 256, 32, 32, bin, bout, reps,
- target_ns);
+ const oneshot_t sha3[] = {
+ {"scalar", kf_scalar_sha3_256},
+ {kf_backend_name(), kf_sha3_256},
+ };
+ const oneshot_t shake[] = {
+ {"scalar", kf_scalar_shake256},
+ {kf_backend_name(), kf_shake256},
+ };
+
+ batch_t batch[4];
+ int nb = 0;
+#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
+ !defined(KECCAK_FAST_NO_AVX2)
+ batch[nb++] = (batch_t){"avx2 x4", kf_avx2_batch, 4};
+#endif
+#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
+ !defined(KECCAK_FAST_NO_AVX512)
+ batch[nb++] = (batch_t){"avx512 x8", kf_avx512_batch, 8};
+#endif
+
+ case_all("SHA3-256 32->32, per hash", sha3, 2, batch, nb, kct_sha3_256,
+ kf_sha3_256_batch, 64, 32, 32, in, out, reps, target_ns);
+ case_all("SHA3-256 64->32, per hash", sha3, 2, batch, nb, kct_sha3_256,
+ kf_sha3_256_batch, 64, 64, 32, in, out, reps, target_ns);
+ case_all("SHAKE256 32->32, per hash", shake, 2, batch, nb, kct_shake256,
+ kf_shake256_batch, 64, 32, 32, in, out, reps, target_ns);
+ case_all("SHAKE256 32->1088, per hash", shake, 2, batch, nb, kct_shake256,
+ kf_shake256_batch, 64, 32, 1088, in, out, reps, target_ns);
printf("sink %llu\n", (unsigned long long)g_sink);
return 0;
diff --git a/src/backend/avx2.c b/src/backend/avx2.c
@@ -100,7 +100,7 @@ static void kf_permute_x4(uint64_t s[4][25]) {
}
__attribute__((target("avx2")))
-static void kf_avx2_batch(uint64_t states[][25], size_t count) {
+void kf_avx2_batch(uint64_t states[][25], size_t count) {
size_t i = 0;
for (; i + 4 <= count; i += 4) {
kf_permute_x4(&states[i]);
diff --git a/src/backend/avx512.c b/src/backend/avx512.c
@@ -96,7 +96,7 @@ static void kf_permute_x8(uint64_t s[8][25]) {
}
__attribute__((target("avx512f")))
-static void kf_avx512_batch(uint64_t states[][25], size_t count) {
+void kf_avx512_batch(uint64_t states[][25], size_t count) {
size_t i = 0;
for (; i + 8 <= count; i += 8) {
kf_permute_x8(&states[i]);
diff --git a/src/backend/scalar_bmi.c b/src/backend/scalar_bmi.c
@@ -22,7 +22,7 @@
#define KF_LINKAGE static
#include "scalar-impl.h"
-static void kf_bmi_batch(uint64_t states[][25], size_t count) {
+void kf_bmi_batch(uint64_t states[][25], size_t count) {
for (size_t i = 0; i < count; i++) {
keccakf(states[i]);
}
diff --git a/src/keccak-fast-internal.h b/src/keccak-fast-internal.h
@@ -42,4 +42,17 @@ int kf_scalar_turboshake256(uint8_t *out, size_t outlen, const uint8_t *in,
void kf_scalar_permute(uint64_t state[25]);
void kf_scalar_batch(uint64_t states[][25], size_t count);
+#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
+ !defined(KECCAK_FAST_NO_BMI)
+void kf_bmi_batch(uint64_t states[][25], size_t count);
+#endif
+#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
+ !defined(KECCAK_FAST_NO_AVX2)
+void kf_avx2_batch(uint64_t states[][25], size_t count);
+#endif
+#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \
+ !defined(KECCAK_FAST_NO_AVX512)
+void kf_avx512_batch(uint64_t states[][25], size_t count);
+#endif
+
#endif