Optimized the P256K1Signer implementation by profiling and eliminating memory allocations in hot paths. The optimizations focused on reusing buffers for frequently called methods instead of allocating on each call.
Before:
After:
Implementation:
pubBuf []byte) to P256K1Gen structBefore:
After:
Implementation:
sigBuf []byte) to P256K1Signer structsig64[:]Before:
After:
Implementation:
ecdhBuf []byte) to P256K1Signer structsharedSecret[:]Before:
After:
Implementation:
Before & After:
Implementation:
| Benchmark | Before (ns/op) | After (ns/op) | Speedup | Before (B/op) | After (B/op) | Reduction |
|---|---|---|---|---|---|---|
| Generate | 44,420 | 44,018 | 1.01x | 289 | 287 | 0.7% |
| InitSec | 54,223 | 28,319 | 1.91x | 257 | 128 | 50.2% |
| InitPub | 5,708 | 5,669 | 1.01x | 32 | 32 | 0% |
| Sign | 55,645 | 56,291 | 0.99x | 640 | 576 | 10% |
| Verify | 136,922 | 134,306 | 1.02x | 97 | 96 | 1% |
| ECDH | 106,611 | 106,638 | 1.00x | 246 | 209 | 15% |
| Pub | 0.52 | 0.25 | 2.08x | 0 | 0 | 0% |
| Gen.Generate | 29,534 | 31,402 | 0.94x | 304 | 304 | 0% |
| Gen.Negate | 27,707 | 27,994 | 0.99x | 192 | 192 | 0% |
| Gen.KeyPairBytes | 23.58 | 4.529 | 5.21x | 32 | 0 | 100% |
The optimizations introduce a subtle API change that users must be aware of:
Methods that now return reusable buffers:
Sign(msg []byte) ([]byte, error)ECDH(pub []byte) ([]byte, error)KeyPairBytes() ([]byte, []byte)Behavior:
Example:
// ❌ WRONG - data may be overwritten
sig1, _ := signer.Sign(msg1)
sig2, _ := signer.Sign(msg2)
// sig1 may now contain sig2's data!
// ✅ CORRECT - copy if you need to retain
sig1, _ := signer.Sign(msg1)
sig1Copy := make([]byte, len(sig1))
copy(sig1Copy, sig1)
sig2, _ := signer.Sign(msg2)
// sig1Copy is safe to use
All existing tests pass without modification, confirming backward compatibility for the common use case where results are used immediately.
cd /home/mleku/src/p256k1.mleku.dev/signer
go test -v
# PASS
To reproduce the profiling results:
# Run benchmarks with profiling
go test -bench=. -benchmem -memprofile=mem.prof -cpuprofile=cpu.prof
# Analyze memory allocations
go tool pprof -top -alloc_space mem.prof
# Detailed line-by-line analysis
go tool pprof -list=P256K1Signer mem.prof