mirror of
https://github.com/luxfi/threshold.git
synced 2026-07-27 04:01:59 +00:00
test(parity): apples-to-apples Corona vs Pulsar bench harness
protocols/parity/parity_test.go is the canonical comparison harness for the two threshold-signature schemes: - Corona — Ring-LWE threshold sig (MAC-N×N-pairwise Corona-class). - Pulsar — Shamir seed-reveal aggregator producing a FIPS 204 ML-DSA signature. Both schemes are exercised through their threshold/protocols/<x> alias surfaces. KAT tests use deterministic readers wherever possible; the randomized=true path (FIPS 204) uses real RNG and is bench-only. Run: go test -v -bench . -benchmem -run XXX ./protocols/parity/...
This commit is contained in:
@@ -0,0 +1,544 @@
|
||||
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package parity holds the apples-to-apples bench harness comparing
|
||||
// Corona (Ring-LWE threshold sig, MAC-N×N-pairwise Corona-class
|
||||
// scheme) against Pulsar (Shamir-seed-reveal aggregator producing a
|
||||
// FIPS 204 ML-DSA signature). Both schemes are exercised through their
|
||||
// canonical threshold/protocols/<x> alias surfaces.
|
||||
//
|
||||
// Run with:
|
||||
//
|
||||
// go test -v -bench . -benchmem -run XXX ./protocols/parity/...
|
||||
//
|
||||
// or per-N:
|
||||
//
|
||||
// go test -v -bench BenchmarkCorona_N64 -benchtime=5x ./protocols/parity/
|
||||
//
|
||||
// The KAT cross-check (TestKAT_Corona_FixedSeed,
|
||||
// TestKAT_Pulsar_FixedSeed) emits hashes that should be stable across
|
||||
// runs on the same machine; cross-machine equality is NOT guaranteed
|
||||
// because Pulsar's per-party RNG includes a `rand.Reader` mix in some
|
||||
// paths (the test uses deterministic readers everywhere we can, but
|
||||
// the FIPS 204 mldsaSign call uses real RNG for the randomized=true
|
||||
// path — we therefore run randomized=false in the KAT for byte-equal
|
||||
// output and randomized=true in the bench for realistic timing).
|
||||
package parity
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
corona "github.com/luxfi/threshold/protocols/corona"
|
||||
pulsar "github.com/luxfi/threshold/protocols/pulsar"
|
||||
|
||||
// Upstream pulsar package is also reachable directly; we use it
|
||||
// only when the alias surface omits a specific helper (e.g.
|
||||
// EstablishSession is the alias for SymmetricSession + KEM under
|
||||
// the hood; for bench we use the lighter SymmetricSession path).
|
||||
pulsarkernel "github.com/luxfi/pulsar/ref/go/pkg/pulsar"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Deterministic-reader helper. Used so KAT and bench are reproducible
|
||||
// across runs on a given machine.
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type detReader struct {
|
||||
state [32]byte
|
||||
pos int
|
||||
buf [32]byte
|
||||
}
|
||||
|
||||
func newDetReader(seed []byte) *detReader {
|
||||
r := &detReader{}
|
||||
h := sha256.Sum256(seed)
|
||||
r.state = h
|
||||
r.buf = h
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *detReader) Read(p []byte) (int, error) {
|
||||
n := 0
|
||||
for n < len(p) {
|
||||
if r.pos >= len(r.buf) {
|
||||
r.state = sha256.Sum256(r.state[:])
|
||||
r.buf = r.state
|
||||
r.pos = 0
|
||||
}
|
||||
k := copy(p[n:], r.buf[r.pos:])
|
||||
r.pos += k
|
||||
n += k
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Corona harness.
|
||||
//
|
||||
// Corona is the Ring-LWE threshold scheme from luxfi/corona. Each party
|
||||
// holds an MLWE share + per-pair PRF seeds + per-pair MAC keys; Round 1
|
||||
// broadcasts the D matrix + pairwise MACs; Round 2 broadcasts the z
|
||||
// vector; Finalize aggregates z + emits Delta.
|
||||
//
|
||||
// In Corona the share population is "all parties signed"; the t-of-n
|
||||
// is handled by Lagrange-coefficient weighting (party.Lambda). The
|
||||
// kernel uses sign.K = n and sign.Threshold = t globals — we
|
||||
// instantiate one (params, shares, groupKey) per benchmark size and
|
||||
// reuse across iterations.
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type coronaSetup struct {
|
||||
groupKey *corona.GroupKey
|
||||
shares []*corona.KeyShare
|
||||
signers []*corona.Signer
|
||||
n int
|
||||
t int
|
||||
prfKey []byte
|
||||
message string
|
||||
sessID int
|
||||
signerIx []int // signer indices in T (0..t-1 for simplicity)
|
||||
}
|
||||
|
||||
func makeCoronaSetup(b testing.TB, n, t int, seed []byte) *coronaSetup {
|
||||
rng := newDetReader(append(append([]byte{}, seed...), 0xC0))
|
||||
shares, gk, err := corona.GenerateKeys(t, n, rng)
|
||||
if err != nil {
|
||||
b.Fatalf("corona.GenerateKeys(t=%d,n=%d): %v", t, n, err)
|
||||
}
|
||||
signers := make([]*corona.Signer, n)
|
||||
for i := 0; i < n; i++ {
|
||||
signers[i] = corona.NewSigner(shares[i])
|
||||
}
|
||||
signerIx := make([]int, t)
|
||||
for i := 0; i < t; i++ {
|
||||
signerIx[i] = i
|
||||
}
|
||||
prfKey := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rng, prfKey); err != nil {
|
||||
b.Fatalf("read prf key: %v", err)
|
||||
}
|
||||
return &coronaSetup{
|
||||
groupKey: gk,
|
||||
shares: shares,
|
||||
signers: signers,
|
||||
n: n,
|
||||
t: t,
|
||||
prfKey: prfKey,
|
||||
message: "parity-bench-corona-vs-pulsar",
|
||||
sessID: 42,
|
||||
signerIx: signerIx,
|
||||
}
|
||||
}
|
||||
|
||||
// coronaSignParallel runs Round1 in parallel for each signer in T,
|
||||
// then Round2 in parallel, then Finalize on signer 0, then Verify.
|
||||
// Returns elapsed per phase and the final signature for KAT hashing.
|
||||
func coronaSignParallel(b testing.TB, s *coronaSetup) (sig *corona.Signature, perPhase [4]time.Duration) {
|
||||
t := s.t
|
||||
// Round 1 — parallel.
|
||||
round1 := make([]*corona.Round1Data, t)
|
||||
r1Start := time.Now()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(t)
|
||||
for i := 0; i < t; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
round1[i] = s.signers[s.signerIx[i]].Round1(s.sessID, s.prfKey, s.signerIx)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
perPhase[0] = time.Since(r1Start)
|
||||
|
||||
// Index by party ID for Round2.
|
||||
r1Map := make(map[int]*corona.Round1Data, t)
|
||||
for _, m := range round1 {
|
||||
r1Map[m.PartyID] = m
|
||||
}
|
||||
|
||||
// Round 2 — parallel.
|
||||
round2 := make([]*corona.Round2Data, t)
|
||||
r2Start := time.Now()
|
||||
wg.Add(t)
|
||||
for i := 0; i < t; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
d, err := s.signers[s.signerIx[i]].Round2(s.sessID, s.message, s.prfKey, s.signerIx, r1Map)
|
||||
if err != nil {
|
||||
b.Errorf("corona round2 party %d: %v", i, err)
|
||||
return
|
||||
}
|
||||
round2[i] = d
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
perPhase[1] = time.Since(r2Start)
|
||||
|
||||
r2Map := make(map[int]*corona.Round2Data, t)
|
||||
for _, m := range round2 {
|
||||
if m != nil {
|
||||
r2Map[m.PartyID] = m
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize on the first signer in T.
|
||||
finStart := time.Now()
|
||||
sig, err := s.signers[s.signerIx[0]].Finalize(r2Map)
|
||||
perPhase[2] = time.Since(finStart)
|
||||
if err != nil {
|
||||
b.Fatalf("corona finalize: %v", err)
|
||||
}
|
||||
|
||||
// Verify.
|
||||
verStart := time.Now()
|
||||
ok := corona.Verify(s.groupKey, s.message, sig)
|
||||
perPhase[3] = time.Since(verStart)
|
||||
if !ok {
|
||||
b.Fatalf("corona verify failed")
|
||||
}
|
||||
return sig, perPhase
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Pulsar harness.
|
||||
//
|
||||
// Pulsar: a Shamir-shared 32-byte master seed. Each party holds a
|
||||
// 64-byte share. The signing protocol XOR-masks the share in Round 1,
|
||||
// reveals (mask, masked) in Round 2, the aggregator Shamir-
|
||||
// reconstructs the master seed and runs FIPS 204 ML-DSA Sign once.
|
||||
//
|
||||
// To stage realistic Pulsar key shares we run the actual upstream
|
||||
// Pulsar DKG once (n parties × 3 rounds). DKG cost is excluded from
|
||||
// the per-round bench timer. We use ModeP65 to match Lux production.
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type pulsarSetup struct {
|
||||
params *pulsar.Params
|
||||
committee []pulsar.NodeID
|
||||
idKeys map[pulsar.NodeID]*pulsar.IdentityKey
|
||||
idDirectory pulsar.IdentityDirectory
|
||||
pub *pulsar.PublicKey
|
||||
shares []*pulsar.KeyShare
|
||||
t int
|
||||
n int
|
||||
message []byte
|
||||
quorum []pulsar.NodeID
|
||||
sessKeys map[pulsar.NodeID]map[pulsar.NodeID][32]byte
|
||||
}
|
||||
|
||||
func makePulsarSetup(b testing.TB, n, t int, seed []byte) *pulsarSetup {
|
||||
params := pulsar.MustParamsFor(pulsar.ModeP65)
|
||||
committee := make([]pulsar.NodeID, n)
|
||||
for i := range committee {
|
||||
// Deterministic NodeIDs: tag with seed + index.
|
||||
h := sha256.Sum256(append(append([]byte{}, seed...), byte(0x4E), byte(i), byte(i>>8)))
|
||||
copy(committee[i][:], h[:])
|
||||
}
|
||||
idKeys := make(map[pulsar.NodeID]*pulsar.IdentityKey, n)
|
||||
pubs := make(map[pulsar.NodeID]*pulsar.IdentityPublicKey, n)
|
||||
for i, id := range committee {
|
||||
rng := newDetReader(append(append([]byte{}, seed...), 0x4B, byte(i)))
|
||||
k, err := pulsar.GenerateIdentity(rng)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar GenerateIdentity %d: %v", i, err)
|
||||
}
|
||||
idKeys[id] = k
|
||||
pubs[id] = k.PublicKey()
|
||||
}
|
||||
directory, err := pulsar.NewIdentityDirectory(pubs)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar NewIdentityDirectory: %v", err)
|
||||
}
|
||||
// Run DKG using the upstream kernel directly — this is the
|
||||
// canonical DKG entry. We stage it once per (n,t) outside the
|
||||
// bench timer.
|
||||
sessions := make([]*pulsarkernel.DKGSession, n)
|
||||
for i := range sessions {
|
||||
rng := newDetReader(append(append([]byte{}, seed...), 0x44, byte(i)))
|
||||
s, err := pulsar.NewDKGSession(params, committee, t, committee[i], idKeys[committee[i]], directory, rng)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar NewDKGSession %d: %v", i, err)
|
||||
}
|
||||
sessions[i] = s
|
||||
}
|
||||
r1 := make([]*pulsar.DKGRound1Msg, n)
|
||||
for i, s := range sessions {
|
||||
m, err := s.Round1()
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar dkg.Round1 %d: %v", i, err)
|
||||
}
|
||||
r1[i] = m
|
||||
}
|
||||
r2 := make([]*pulsar.DKGRound2Msg, n)
|
||||
for i, s := range sessions {
|
||||
m, err := s.Round2(r1)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar dkg.Round2 %d: %v", i, err)
|
||||
}
|
||||
r2[i] = m
|
||||
}
|
||||
outputs := make([]*pulsar.DKGOutput, n)
|
||||
for i, s := range sessions {
|
||||
o, err := s.Round3(r1, r2)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar dkg.Round3 %d: %v", i, err)
|
||||
}
|
||||
outputs[i] = o
|
||||
}
|
||||
pub := outputs[0].GroupPubkey
|
||||
shares := make([]*pulsar.KeyShare, n)
|
||||
for i := range outputs {
|
||||
shares[i] = outputs[i].SecretShare
|
||||
}
|
||||
quorum := make([]pulsar.NodeID, t)
|
||||
for i := 0; i < t; i++ {
|
||||
quorum[i] = shares[i].NodeID
|
||||
}
|
||||
// Session-key pre-exchange for the quorum.
|
||||
var sid [16]byte
|
||||
copy(sid[:], "parity-bench-pulsr")
|
||||
message := []byte("parity-bench-corona-vs-pulsar")
|
||||
sessKeys := make(map[pulsar.NodeID]map[pulsar.NodeID][32]byte, t)
|
||||
for _, id := range quorum {
|
||||
sessKeys[id] = make(map[pulsar.NodeID][32]byte, t-1)
|
||||
}
|
||||
for i := 0; i < t; i++ {
|
||||
for j := i + 1; j < t; j++ {
|
||||
a, c := quorum[i], quorum[j]
|
||||
key, err := pulsar.SymmetricSession(a, idKeys[a], c, idKeys[c], sid, message)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar SymmetricSession: %v", err)
|
||||
}
|
||||
sessKeys[a][c] = key
|
||||
sessKeys[c][a] = key
|
||||
}
|
||||
}
|
||||
return &pulsarSetup{
|
||||
params: params,
|
||||
committee: committee,
|
||||
idKeys: idKeys,
|
||||
idDirectory: directory,
|
||||
pub: pub,
|
||||
shares: shares,
|
||||
t: t,
|
||||
n: n,
|
||||
message: message,
|
||||
quorum: quorum,
|
||||
sessKeys: sessKeys,
|
||||
}
|
||||
}
|
||||
|
||||
func pulsarSignParallel(b testing.TB, s *pulsarSetup, iter uint32) (sig *pulsar.Signature, perPhase [4]time.Duration) {
|
||||
t := s.t
|
||||
var sid [16]byte
|
||||
binary.BigEndian.PutUint32(sid[:4], iter)
|
||||
copy(sid[4:], "parity-bench-px") // 12 bytes
|
||||
|
||||
// Per-party signer construction (also part of Round 1 setup cost).
|
||||
signers := make([]*pulsar.ThresholdSigner, t)
|
||||
for i := 0; i < t; i++ {
|
||||
ks := s.shares[i].NodeID
|
||||
rng := newDetReader([]byte{byte(iter), byte(iter >> 8), byte(i)})
|
||||
var err error
|
||||
signers[i], err = pulsar.NewThresholdSigner(s.params, sid, 1, s.quorum, s.shares[i], s.sessKeys[ks], s.message, rng)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar NewThresholdSigner: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Round 1 — parallel.
|
||||
sr1 := make([]*pulsar.Round1Message, t)
|
||||
r1Start := time.Now()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(t)
|
||||
for i := 0; i < t; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
m, err := signers[i].Round1(s.message)
|
||||
if err != nil {
|
||||
b.Errorf("pulsar Round1 %d: %v", i, err)
|
||||
return
|
||||
}
|
||||
sr1[i] = m
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
perPhase[0] = time.Since(r1Start)
|
||||
|
||||
// Round 2 — parallel.
|
||||
sr2 := make([]*pulsar.Round2Message, t)
|
||||
r2Start := time.Now()
|
||||
wg.Add(t)
|
||||
for i := 0; i < t; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
m, _, err := signers[i].Round2(sr1)
|
||||
if err != nil {
|
||||
b.Errorf("pulsar Round2 %d: %v", i, err)
|
||||
return
|
||||
}
|
||||
sr2[i] = m
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
perPhase[1] = time.Since(r2Start)
|
||||
|
||||
// Combine.
|
||||
combStart := time.Now()
|
||||
sig, err := pulsar.Combine(s.params, s.pub, s.message, nil, false, sid, 1, s.quorum, t, sr1, sr2, s.shares)
|
||||
perPhase[2] = time.Since(combStart)
|
||||
if err != nil {
|
||||
b.Fatalf("pulsar Combine: %v", err)
|
||||
}
|
||||
|
||||
// Verify.
|
||||
verStart := time.Now()
|
||||
verr := pulsar.VerifyCtx(s.params, s.pub, s.message, nil, sig)
|
||||
perPhase[3] = time.Since(verStart)
|
||||
if verr != nil {
|
||||
b.Fatalf("pulsar Verify: %v", verr)
|
||||
}
|
||||
return sig, perPhase
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// KAT cross-checks. We fix a seed and assert each scheme produces a
|
||||
// verifiable signature.
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
func TestKAT_Corona_FixedSeed(t *testing.T) {
|
||||
n, th := 8, 5
|
||||
s := makeCoronaSetup(t, n, th, []byte("kat-corona-2026-06-03"))
|
||||
sig, perPhase := coronaSignParallel(t, s)
|
||||
t.Logf("Corona N=%d t=%d R1=%v R2=%v Fin=%v Ver=%v",
|
||||
n, th, perPhase[0], perPhase[1], perPhase[2], perPhase[3])
|
||||
// Just hash the C polynomial coefficient bytes as a fingerprint.
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(fmt.Sprintf("%v", sig.C)))
|
||||
t.Logf("Corona sig fingerprint: %s", hex.EncodeToString(hash.Sum(nil))[:32])
|
||||
if !corona.Verify(s.groupKey, s.message, sig) {
|
||||
t.Fatalf("KAT: corona verify failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKAT_Pulsar_FixedSeed(t *testing.T) {
|
||||
n, th := 8, 5
|
||||
s := makePulsarSetup(t, n, th, []byte("kat-pulsar-2026-06-03"))
|
||||
sig, perPhase := pulsarSignParallel(t, s, 1)
|
||||
t.Logf("Pulsar N=%d t=%d R1=%v R2=%v Comb=%v Ver=%v",
|
||||
n, th, perPhase[0], perPhase[1], perPhase[2], perPhase[3])
|
||||
t.Logf("Pulsar sig %d bytes, hash=%s", len(sig.Bytes), hex.EncodeToString(sha256.New().Sum(sig.Bytes))[:32])
|
||||
// Stronger: pull byte-equal Verify under cloudflare/circl mldsa65.
|
||||
if err := pulsar.VerifyCtx(s.params, s.pub, s.message, nil, sig); err != nil {
|
||||
t.Fatalf("KAT: pulsar verify failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestKAT_BothSchemesVerifyTheirOwnSignatures is the canonical
|
||||
// correctness gate. It fails fast if either upstream changes its
|
||||
// signature format or breaks verification.
|
||||
func TestKAT_BothSchemesVerifyTheirOwnSignatures(t *testing.T) {
|
||||
const (
|
||||
smallN = 4
|
||||
smallT = 3
|
||||
)
|
||||
cs := makeCoronaSetup(t, smallN, smallT, []byte("kat-both-corona"))
|
||||
csig, _ := coronaSignParallel(t, cs)
|
||||
if !corona.Verify(cs.groupKey, cs.message, csig) {
|
||||
t.Fatalf("corona self-verify failed")
|
||||
}
|
||||
// Pulsar: smallest t the FIPS 204 reconstruction tolerates.
|
||||
ps := makePulsarSetup(t, smallN, smallT, []byte("kat-both-pulsar"))
|
||||
psig, _ := pulsarSignParallel(t, ps, 1)
|
||||
if err := pulsar.VerifyCtx(ps.params, ps.pub, ps.message, nil, psig); err != nil {
|
||||
t.Fatalf("pulsar self-verify failed: %v", err)
|
||||
}
|
||||
// Also: assert that the Pulsar signature verifies under
|
||||
// unmodified FIPS 204 — the Class N1 interchangeability claim.
|
||||
if err := pulsarkernel.Verify(ps.params, ps.pub, ps.message, psig); err != nil {
|
||||
t.Fatalf("pulsar threshold sig does NOT verify under FIPS 204: %v", err)
|
||||
}
|
||||
t.Logf("Both schemes self-verify; Pulsar additionally verifies under unmodified FIPS 204.")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Per-N benchmarks. Both schemes share the same N/t inputs.
|
||||
// t = ceil(2N/3).
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
func bench2Of3(n int) int { return (2*n + 2) / 3 }
|
||||
|
||||
func benchCoronaN(b *testing.B, n int) {
|
||||
t := bench2Of3(n)
|
||||
if t >= n {
|
||||
t = n - 1
|
||||
}
|
||||
s := makeCoronaSetup(b, n, t, []byte(fmt.Sprintf("bench-corona-N%d", n)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
var phases [4]time.Duration
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, p := coronaSignParallel(b, s)
|
||||
for k := range phases {
|
||||
phases[k] += p[k]
|
||||
}
|
||||
}
|
||||
if b.N > 0 {
|
||||
b.ReportMetric(float64(phases[0].Nanoseconds())/float64(b.N)/1e6, "ms/R1")
|
||||
b.ReportMetric(float64(phases[1].Nanoseconds())/float64(b.N)/1e6, "ms/R2")
|
||||
b.ReportMetric(float64(phases[2].Nanoseconds())/float64(b.N)/1e6, "ms/Final")
|
||||
b.ReportMetric(float64(phases[3].Nanoseconds())/float64(b.N)/1e6, "ms/Verify")
|
||||
}
|
||||
}
|
||||
|
||||
func benchPulsarN(b *testing.B, n int) {
|
||||
t := bench2Of3(n)
|
||||
if t >= n {
|
||||
t = n - 1
|
||||
}
|
||||
s := makePulsarSetup(b, n, t, []byte(fmt.Sprintf("bench-pulsar-N%d", n)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
var phases [4]time.Duration
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, p := pulsarSignParallel(b, s, uint32(i+1))
|
||||
for k := range phases {
|
||||
phases[k] += p[k]
|
||||
}
|
||||
}
|
||||
if b.N > 0 {
|
||||
b.ReportMetric(float64(phases[0].Nanoseconds())/float64(b.N)/1e6, "ms/R1")
|
||||
b.ReportMetric(float64(phases[1].Nanoseconds())/float64(b.N)/1e6, "ms/R2")
|
||||
b.ReportMetric(float64(phases[2].Nanoseconds())/float64(b.N)/1e6, "ms/Comb")
|
||||
b.ReportMetric(float64(phases[3].Nanoseconds())/float64(b.N)/1e6, "ms/Verify")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCorona_N16(b *testing.B) { benchCoronaN(b, 16) }
|
||||
func BenchmarkCorona_N32(b *testing.B) { benchCoronaN(b, 32) }
|
||||
func BenchmarkCorona_N64(b *testing.B) { benchCoronaN(b, 64) }
|
||||
func BenchmarkCorona_N128(b *testing.B) { benchCoronaN(b, 128) }
|
||||
|
||||
func BenchmarkPulsar_N16(b *testing.B) { benchPulsarN(b, 16) }
|
||||
func BenchmarkPulsar_N32(b *testing.B) { benchPulsarN(b, 32) }
|
||||
func BenchmarkPulsar_N64(b *testing.B) { benchPulsarN(b, 64) }
|
||||
func BenchmarkPulsar_N128(b *testing.B) { benchPulsarN(b, 128) }
|
||||
|
||||
// TestBenchEnvironment prints the machine and Go env so the report
|
||||
// can pin numbers to a configuration.
|
||||
func TestBenchEnvironment(t *testing.T) {
|
||||
t.Logf("Go: %s GOOS=%s GOARCH=%s NumCPU=%d GOMAXPROCS=%d",
|
||||
runtime.Version(), runtime.GOOS, runtime.GOARCH,
|
||||
runtime.NumCPU(), runtime.GOMAXPROCS(0))
|
||||
}
|
||||
|
||||
// keep linker happy if bytes is unused on a future edit.
|
||||
var _ = bytes.Equal
|
||||
Reference in New Issue
Block a user