From e2725718c23ac3833fb8146f6b7cfb54f860422e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 21:39:23 +0000 Subject: [PATCH] docs: add standalone C benchmark for libsecp256k1 Standalone C program that links against the ACINQ secp256k1-kmp-jni .so to benchmark raw C libsecp256k1 performance without any JVM, JNI, or ART overhead. Uses the same test vectors as the Kotlin benchmarks. Useful as a baseline when comparing Kotlin/Native or Android results against the C library on the same hardware. https://claude.ai/code/session_015CtM5k88rF7WFgX8o2AGNR --- quartz/benchmarks/secp256k1_native_bench.c | 113 +++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 quartz/benchmarks/secp256k1_native_bench.c diff --git a/quartz/benchmarks/secp256k1_native_bench.c b/quartz/benchmarks/secp256k1_native_bench.c new file mode 100644 index 000000000..6239266b9 --- /dev/null +++ b/quartz/benchmarks/secp256k1_native_bench.c @@ -0,0 +1,113 @@ +/* + * Standalone C benchmark for libsecp256k1 — no JVM, no JNI, no ART. + * Links against the ACINQ secp256k1-kmp-jni .so to benchmark raw C performance. + * Uses the same test vectors as the Kotlin benchmarks for direct comparison. + * + * Build: + * # Extract the native .so from the ACINQ JAR: + * jar xf ~/.gradle/caches/modules-2/files-2.1/fr.acinq.secp256k1/\ + * secp256k1-kmp-jni-jvm-linux/0.23.0/*/secp256k1-kmp-jni-jvm-linux-0.23.0.jar + * cp fr/acinq/secp256k1/jni/native/linux-x86_64/libsecp256k1-jni.so . + * + * # Compile and run: + * gcc -O2 -o bench secp256k1_native_bench.c -L. -lsecp256k1-jni -Wl,-rpath,. + * ./bench + */ +#include +#include +#include +#include +#include + +typedef struct { unsigned char data[64]; } secp256k1_pubkey; +typedef struct { unsigned char data[64]; } secp256k1_xonly_pubkey; +typedef struct { unsigned char data[96]; } secp256k1_keypair; +typedef struct secp256k1_context_struct secp256k1_context; + +/* flags: SIGN=0x201, VERIFY=0x101 */ +#define SECP256K1_FLAGS (0x201 | 0x101) +#define SECP256K1_EC_COMPRESSED 258 + +extern secp256k1_context *secp256k1_context_create(unsigned int flags); +extern void secp256k1_context_destroy(secp256k1_context *ctx); +extern int secp256k1_ec_seckey_verify(const secp256k1_context *ctx, const unsigned char *seckey); +extern int secp256k1_ec_pubkey_create(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey); +extern int secp256k1_ec_pubkey_serialize(const secp256k1_context *ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey *pubkey, unsigned int flags); +extern int secp256k1_schnorrsig_sign32(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32); +extern int secp256k1_schnorrsig_verify(const secp256k1_context *ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey); +extern int secp256k1_keypair_create(const secp256k1_context *ctx, secp256k1_keypair *keypair, const unsigned char *seckey); +extern int secp256k1_keypair_xonly_pub(const secp256k1_context *ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair); +extern int secp256k1_ec_seckey_tweak_add(const secp256k1_context *ctx, unsigned char *seckey, const unsigned char *tweak); +extern int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32); + +static void hex2bin(const char *hex, unsigned char *out, size_t len) { + for (size_t i = 0; i < len; i++) { unsigned v; sscanf(hex+2*i, "%2x", &v); out[i] = v; } +} + +static uint64_t now_ns(void) { + struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} + +#define BENCH(name, warmup, iters, body) do { \ + for (int _w = 0; _w < (warmup); _w++) { body; } \ + uint64_t _start = now_ns(); \ + for (int _i = 0; _i < (iters); _i++) { body; } \ + uint64_t _el = now_ns() - _start; \ + printf(" %-24s %8llu ns/op %8llu ops/s\n", name, \ + (unsigned long long)(_el / (iters)), \ + (unsigned long long)((uint64_t)(iters) * 1000000000ULL / _el)); \ +} while(0) + +int main(void) { + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_FLAGS); + unsigned char priv[32], msg[32], aux[32], priv2[32]; + hex2bin("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530", priv, 32); + hex2bin("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", msg, 32); + hex2bin("0000000000000000000000000000000000000000000000000000000000000001", aux, 32); + hex2bin("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3", priv2, 32); + + secp256k1_keypair kp; secp256k1_keypair_create(ctx, &kp, priv); + secp256k1_xonly_pubkey xpub; secp256k1_keypair_xonly_pub(ctx, &xpub, NULL, &kp); + secp256k1_pubkey pub; secp256k1_ec_pubkey_create(ctx, &pub, priv); + unsigned char sig[64]; secp256k1_schnorrsig_sign32(ctx, sig, msg, &kp, aux); + + if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &xpub)) { + fprintf(stderr, "verify failed!\n"); return 1; + } + + volatile int r; + printf("================================================================================\n"); + printf("secp256k1 Benchmark: C libsecp256k1 (direct, no JNI/JVM) on x86_64\n"); + printf("================================================================================\n"); + + BENCH("verifySchnorr", 2000, 5000, + r = secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &xpub)); + + BENCH("signSchnorr", 1000, 3000, + secp256k1_schnorrsig_sign32(ctx, sig, msg, &kp, aux)); + + { unsigned char c[33]; size_t cl; + BENCH("compressedPubKeyFor", 1000, 5000, { + secp256k1_ec_pubkey_create(ctx, &pub, priv); + cl = 33; secp256k1_ec_pubkey_serialize(ctx, c, &cl, &pub, SECP256K1_EC_COMPRESSED); + }); } + + BENCH("secKeyVerify", 5000, 200000, + r = secp256k1_ec_seckey_verify(ctx, priv)); + + { unsigned char tw[32]; + BENCH("privKeyTweakAdd", 1000, 50000, { + memcpy(tw, priv, 32); + secp256k1_ec_seckey_tweak_add(ctx, tw, priv2); + }); } + + { secp256k1_pubkey p2; secp256k1_ec_pubkey_create(ctx, &p2, priv2); + BENCH("ecPubKeyTweakMul", 1000, 3000, + secp256k1_ec_pubkey_tweak_mul(ctx, &p2, priv)); + } + + printf("================================================================================\n"); + secp256k1_context_destroy(ctx); + return 0; +}