avx2_bench_test.go raw
1 //go:build !nocgo && !js && !wasm && !tinygo && !wasm32
2
3 package bench
4
5 import (
6 "crypto/rand"
7 "testing"
8
9 "next.orly.dev/pkg/p256k1"
10 "next.orly.dev/pkg/p256k1/signer"
11 )
12
13 // This file contains benchmarks comparing:
14 // 1. P256K1 Pure Go implementation
15 // 2. P256K1 with AVX2 scalar operations (where applicable)
16 // 3. libsecp256k1.so via purego (if available)
17
18 var (
19 avxBenchSeckey []byte
20 avxBenchMsghash []byte
21 avxBenchSigner *signer.P256K1Signer
22 avxBenchSigner2 *signer.P256K1Signer
23 avxBenchSig []byte
24 avxBenchLibSecp *p256k1.LibSecp256k1
25 )
26
27 func initAVXBenchData() {
28 if avxBenchSeckey == nil {
29 avxBenchSeckey = []byte{
30 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
31 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
32 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
33 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
34 }
35
36 for {
37 testSigner := signer.NewP256K1Signer()
38 if err := testSigner.InitSec(avxBenchSeckey); err == nil {
39 break
40 }
41 if _, err := rand.Read(avxBenchSeckey); err != nil {
42 panic(err)
43 }
44 }
45
46 avxBenchMsghash = make([]byte, 32)
47 if _, err := rand.Read(avxBenchMsghash); err != nil {
48 panic(err)
49 }
50 }
51
52 // Setup P256K1Signer
53 s := signer.NewP256K1Signer()
54 if err := s.InitSec(avxBenchSeckey); err != nil {
55 panic(err)
56 }
57 avxBenchSigner = s
58
59 var err error
60 avxBenchSig, err = s.Sign(avxBenchMsghash)
61 if err != nil {
62 panic(err)
63 }
64
65 // Generate second key pair for ECDH
66 seckey2 := make([]byte, 32)
67 for {
68 if _, err := rand.Read(seckey2); err != nil {
69 panic(err)
70 }
71 testSigner := signer.NewP256K1Signer()
72 if err := testSigner.InitSec(seckey2); err == nil {
73 break
74 }
75 }
76
77 s2 := signer.NewP256K1Signer()
78 if err := s2.InitSec(seckey2); err != nil {
79 panic(err)
80 }
81 avxBenchSigner2 = s2
82
83 // Try to load libsecp256k1
84 avxBenchLibSecp, _ = p256k1.GetLibSecp256k1()
85 }
86
87 // Pure Go benchmarks (AVX2 disabled)
88 func BenchmarkPureGo_PubkeyDerivation(b *testing.B) {
89 if avxBenchSeckey == nil {
90 initAVXBenchData()
91 }
92
93 p256k1.SetAVX2Enabled(false)
94 defer p256k1.SetAVX2Enabled(true)
95
96 b.ResetTimer()
97 for i := 0; i < b.N; i++ {
98 s := signer.NewP256K1Signer()
99 if err := s.InitSec(avxBenchSeckey); err != nil {
100 b.Fatalf("failed to create signer: %v", err)
101 }
102 _ = s.Pub()
103 }
104 }
105
106 func BenchmarkPureGo_Sign(b *testing.B) {
107 if avxBenchSeckey == nil {
108 initAVXBenchData()
109 }
110
111 p256k1.SetAVX2Enabled(false)
112 defer p256k1.SetAVX2Enabled(true)
113
114 b.ResetTimer()
115 for i := 0; i < b.N; i++ {
116 _, err := avxBenchSigner.Sign(avxBenchMsghash)
117 if err != nil {
118 b.Fatalf("failed to sign: %v", err)
119 }
120 }
121 }
122
123 func BenchmarkPureGo_Verify(b *testing.B) {
124 if avxBenchSeckey == nil {
125 initAVXBenchData()
126 }
127
128 p256k1.SetAVX2Enabled(false)
129 defer p256k1.SetAVX2Enabled(true)
130
131 b.ResetTimer()
132 for i := 0; i < b.N; i++ {
133 verifier := signer.NewP256K1Signer()
134 if err := verifier.InitPub(avxBenchSigner.Pub()); err != nil {
135 b.Fatalf("failed to create verifier: %v", err)
136 }
137 valid, err := verifier.Verify(avxBenchMsghash, avxBenchSig)
138 if err != nil {
139 b.Fatalf("verification error: %v", err)
140 }
141 if !valid {
142 b.Fatalf("verification failed")
143 }
144 }
145 }
146
147 func BenchmarkPureGo_ECDH(b *testing.B) {
148 if avxBenchSeckey == nil {
149 initAVXBenchData()
150 }
151
152 p256k1.SetAVX2Enabled(false)
153 defer p256k1.SetAVX2Enabled(true)
154
155 b.ResetTimer()
156 for i := 0; i < b.N; i++ {
157 _, err := avxBenchSigner.ECDH(avxBenchSigner2.Pub())
158 if err != nil {
159 b.Fatalf("ECDH failed: %v", err)
160 }
161 }
162 }
163
164 // AVX2-enabled benchmarks
165 func BenchmarkAVX2_PubkeyDerivation(b *testing.B) {
166 if avxBenchSeckey == nil {
167 initAVXBenchData()
168 }
169
170 if !p256k1.HasAVX2CPU() {
171 b.Skip("AVX2 not available")
172 }
173
174 p256k1.SetAVX2Enabled(true)
175
176 b.ResetTimer()
177 for i := 0; i < b.N; i++ {
178 s := signer.NewP256K1Signer()
179 if err := s.InitSec(avxBenchSeckey); err != nil {
180 b.Fatalf("failed to create signer: %v", err)
181 }
182 _ = s.Pub()
183 }
184 }
185
186 func BenchmarkAVX2_Sign(b *testing.B) {
187 if avxBenchSeckey == nil {
188 initAVXBenchData()
189 }
190
191 if !p256k1.HasAVX2CPU() {
192 b.Skip("AVX2 not available")
193 }
194
195 p256k1.SetAVX2Enabled(true)
196
197 b.ResetTimer()
198 for i := 0; i < b.N; i++ {
199 _, err := avxBenchSigner.Sign(avxBenchMsghash)
200 if err != nil {
201 b.Fatalf("failed to sign: %v", err)
202 }
203 }
204 }
205
206 func BenchmarkAVX2_Verify(b *testing.B) {
207 if avxBenchSeckey == nil {
208 initAVXBenchData()
209 }
210
211 if !p256k1.HasAVX2CPU() {
212 b.Skip("AVX2 not available")
213 }
214
215 p256k1.SetAVX2Enabled(true)
216
217 b.ResetTimer()
218 for i := 0; i < b.N; i++ {
219 verifier := signer.NewP256K1Signer()
220 if err := verifier.InitPub(avxBenchSigner.Pub()); err != nil {
221 b.Fatalf("failed to create verifier: %v", err)
222 }
223 valid, err := verifier.Verify(avxBenchMsghash, avxBenchSig)
224 if err != nil {
225 b.Fatalf("verification error: %v", err)
226 }
227 if !valid {
228 b.Fatalf("verification failed")
229 }
230 }
231 }
232
233 func BenchmarkAVX2_ECDH(b *testing.B) {
234 if avxBenchSeckey == nil {
235 initAVXBenchData()
236 }
237
238 if !p256k1.HasAVX2CPU() {
239 b.Skip("AVX2 not available")
240 }
241
242 p256k1.SetAVX2Enabled(true)
243
244 b.ResetTimer()
245 for i := 0; i < b.N; i++ {
246 _, err := avxBenchSigner.ECDH(avxBenchSigner2.Pub())
247 if err != nil {
248 b.Fatalf("ECDH failed: %v", err)
249 }
250 }
251 }
252
253 // libsecp256k1.so benchmarks via purego
254 func BenchmarkLibSecp_Sign(b *testing.B) {
255 if avxBenchSeckey == nil {
256 initAVXBenchData()
257 }
258
259 if avxBenchLibSecp == nil || !avxBenchLibSecp.IsLoaded() {
260 b.Skip("libsecp256k1.so not available")
261 }
262
263 b.ResetTimer()
264 for i := 0; i < b.N; i++ {
265 _, err := avxBenchLibSecp.SchnorrSign(avxBenchMsghash, avxBenchSeckey)
266 if err != nil {
267 b.Fatalf("signing failed: %v", err)
268 }
269 }
270 }
271
272 func BenchmarkLibSecp_PubkeyDerivation(b *testing.B) {
273 if avxBenchSeckey == nil {
274 initAVXBenchData()
275 }
276
277 if avxBenchLibSecp == nil || !avxBenchLibSecp.IsLoaded() {
278 b.Skip("libsecp256k1.so not available")
279 }
280
281 b.ResetTimer()
282 for i := 0; i < b.N; i++ {
283 _, err := avxBenchLibSecp.CreatePubkey(avxBenchSeckey)
284 if err != nil {
285 b.Fatalf("pubkey creation failed: %v", err)
286 }
287 }
288 }
289
290 func BenchmarkLibSecp_Verify(b *testing.B) {
291 if avxBenchSeckey == nil {
292 initAVXBenchData()
293 }
294
295 if avxBenchLibSecp == nil || !avxBenchLibSecp.IsLoaded() {
296 b.Skip("libsecp256k1.so not available")
297 }
298
299 // Sign with libsecp to get compatible signature
300 sig, err := avxBenchLibSecp.SchnorrSign(avxBenchMsghash, avxBenchSeckey)
301 if err != nil {
302 b.Fatalf("signing failed: %v", err)
303 }
304
305 pubkey, err := avxBenchLibSecp.CreatePubkey(avxBenchSeckey)
306 if err != nil {
307 b.Fatalf("pubkey creation failed: %v", err)
308 }
309
310 b.ResetTimer()
311 for i := 0; i < b.N; i++ {
312 if !avxBenchLibSecp.SchnorrVerify(sig, avxBenchMsghash, pubkey) {
313 b.Fatalf("verification failed")
314 }
315 }
316 }
317