126 lines
5.8 KiB
C
126 lines
5.8 KiB
C
// 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, then:
|
|
// gcc -O2 -o bench secp256k1_native_bench.c -L. -lsecp256k1-jni -Wl,-rpath,.
|
|
// ./bench
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <stdint.h>
|
|
|
|
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;
|
|
}
|
|
|
|
/* Format a uint64 with comma-separated thousands into a static buffer.
|
|
Uses a ring of 4 buffers so two calls can be used in one printf. */
|
|
static const char *fmt_comma(uint64_t v) {
|
|
static char bufs[4][32];
|
|
static int idx = 0;
|
|
char *buf = bufs[idx++ & 3];
|
|
char tmp[32];
|
|
int n = snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)v);
|
|
int commas = (n - 1) / 3;
|
|
int len = n + commas;
|
|
buf[len] = '\0';
|
|
int src = n - 1, dst = len - 1, grp = 0;
|
|
while (src >= 0) {
|
|
buf[dst--] = tmp[src--];
|
|
if (++grp == 3 && src >= 0) { buf[dst--] = ','; grp = 0; }
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
#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 %10s ns/op %10s ops/s\n", name, \
|
|
fmt_comma(_el / (iters)), \
|
|
fmt_comma((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;
|
|
}
|