Files
p256k1/bench/avx2_bench_test.go

317 lines
6.4 KiB
Go

//go:build !nocgo
package bench
import (
"crypto/rand"
"testing"
"p256k1.mleku.dev"
"p256k1.mleku.dev/signer"
)
// This file contains benchmarks comparing:
// 1. P256K1 Pure Go implementation
// 2. P256K1 with AVX2 scalar operations (where applicable)
// 3. libsecp256k1.so via purego (if available)
var (
avxBenchSeckey []byte
avxBenchMsghash []byte
avxBenchSigner *signer.P256K1Signer
avxBenchSigner2 *signer.P256K1Signer
avxBenchSig []byte
avxBenchLibSecp *p256k1.LibSecp256k1
)
func initAVXBenchData() {
if avxBenchSeckey == nil {
avxBenchSeckey = []byte{
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
}
for {
testSigner := signer.NewP256K1Signer()
if err := testSigner.InitSec(avxBenchSeckey); err == nil {
break
}
if _, err := rand.Read(avxBenchSeckey); err != nil {
panic(err)
}
}
avxBenchMsghash = make([]byte, 32)
if _, err := rand.Read(avxBenchMsghash); err != nil {
panic(err)
}
}
// Setup P256K1Signer
s := signer.NewP256K1Signer()
if err := s.InitSec(avxBenchSeckey); err != nil {
panic(err)
}
avxBenchSigner = s
var err error
avxBenchSig, err = s.Sign(avxBenchMsghash)
if err != nil {
panic(err)
}
// Generate second key pair for ECDH
seckey2 := make([]byte, 32)
for {
if _, err := rand.Read(seckey2); err != nil {
panic(err)
}
testSigner := signer.NewP256K1Signer()
if err := testSigner.InitSec(seckey2); err == nil {
break
}
}
s2 := signer.NewP256K1Signer()
if err := s2.InitSec(seckey2); err != nil {
panic(err)
}
avxBenchSigner2 = s2
// Try to load libsecp256k1
avxBenchLibSecp, _ = p256k1.GetLibSecp256k1()
}
// Pure Go benchmarks (AVX2 disabled)
func BenchmarkPureGo_PubkeyDerivation(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
p256k1.SetAVX2Enabled(false)
defer p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
s := signer.NewP256K1Signer()
if err := s.InitSec(avxBenchSeckey); err != nil {
b.Fatalf("failed to create signer: %v", err)
}
_ = s.Pub()
}
}
func BenchmarkPureGo_Sign(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
p256k1.SetAVX2Enabled(false)
defer p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := avxBenchSigner.Sign(avxBenchMsghash)
if err != nil {
b.Fatalf("failed to sign: %v", err)
}
}
}
func BenchmarkPureGo_Verify(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
p256k1.SetAVX2Enabled(false)
defer p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
verifier := signer.NewP256K1Signer()
if err := verifier.InitPub(avxBenchSigner.Pub()); err != nil {
b.Fatalf("failed to create verifier: %v", err)
}
valid, err := verifier.Verify(avxBenchMsghash, avxBenchSig)
if err != nil {
b.Fatalf("verification error: %v", err)
}
if !valid {
b.Fatalf("verification failed")
}
}
}
func BenchmarkPureGo_ECDH(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
p256k1.SetAVX2Enabled(false)
defer p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := avxBenchSigner.ECDH(avxBenchSigner2.Pub())
if err != nil {
b.Fatalf("ECDH failed: %v", err)
}
}
}
// AVX2-enabled benchmarks
func BenchmarkAVX2_PubkeyDerivation(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if !p256k1.HasAVX2CPU() {
b.Skip("AVX2 not available")
}
p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
s := signer.NewP256K1Signer()
if err := s.InitSec(avxBenchSeckey); err != nil {
b.Fatalf("failed to create signer: %v", err)
}
_ = s.Pub()
}
}
func BenchmarkAVX2_Sign(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if !p256k1.HasAVX2CPU() {
b.Skip("AVX2 not available")
}
p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := avxBenchSigner.Sign(avxBenchMsghash)
if err != nil {
b.Fatalf("failed to sign: %v", err)
}
}
}
func BenchmarkAVX2_Verify(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if !p256k1.HasAVX2CPU() {
b.Skip("AVX2 not available")
}
p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
verifier := signer.NewP256K1Signer()
if err := verifier.InitPub(avxBenchSigner.Pub()); err != nil {
b.Fatalf("failed to create verifier: %v", err)
}
valid, err := verifier.Verify(avxBenchMsghash, avxBenchSig)
if err != nil {
b.Fatalf("verification error: %v", err)
}
if !valid {
b.Fatalf("verification failed")
}
}
}
func BenchmarkAVX2_ECDH(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if !p256k1.HasAVX2CPU() {
b.Skip("AVX2 not available")
}
p256k1.SetAVX2Enabled(true)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := avxBenchSigner.ECDH(avxBenchSigner2.Pub())
if err != nil {
b.Fatalf("ECDH failed: %v", err)
}
}
}
// libsecp256k1.so benchmarks via purego
func BenchmarkLibSecp_Sign(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if avxBenchLibSecp == nil || !avxBenchLibSecp.IsLoaded() {
b.Skip("libsecp256k1.so not available")
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := avxBenchLibSecp.SchnorrSign(avxBenchMsghash, avxBenchSeckey)
if err != nil {
b.Fatalf("signing failed: %v", err)
}
}
}
func BenchmarkLibSecp_PubkeyDerivation(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if avxBenchLibSecp == nil || !avxBenchLibSecp.IsLoaded() {
b.Skip("libsecp256k1.so not available")
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := avxBenchLibSecp.CreatePubkey(avxBenchSeckey)
if err != nil {
b.Fatalf("pubkey creation failed: %v", err)
}
}
}
func BenchmarkLibSecp_Verify(b *testing.B) {
if avxBenchSeckey == nil {
initAVXBenchData()
}
if avxBenchLibSecp == nil || !avxBenchLibSecp.IsLoaded() {
b.Skip("libsecp256k1.so not available")
}
// Sign with libsecp to get compatible signature
sig, err := avxBenchLibSecp.SchnorrSign(avxBenchMsghash, avxBenchSeckey)
if err != nil {
b.Fatalf("signing failed: %v", err)
}
pubkey, err := avxBenchLibSecp.CreatePubkey(avxBenchSeckey)
if err != nil {
b.Fatalf("pubkey creation failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !avxBenchLibSecp.SchnorrVerify(sig, avxBenchMsghash, pubkey) {
b.Fatalf("verification failed")
}
}
}