Files
threshold/pkg/protocol/handler_benchmark_test.go

361 lines
9.0 KiB
Go

package protocol_test
import (
"context"
"fmt"
"runtime"
"sync"
"testing"
"time"
"github.com/luxfi/log"
"github.com/luxfi/metric"
"github.com/luxfi/threshold/internal/test"
"github.com/luxfi/threshold/pkg/math/curve"
"github.com/luxfi/threshold/pkg/party"
"github.com/luxfi/threshold/pkg/pool"
"github.com/luxfi/threshold/pkg/protocol"
"github.com/luxfi/threshold/protocols/lss"
"github.com/luxfi/threshold/protocols/lss/config"
"github.com/stretchr/testify/require"
)
// BenchmarkHandler compares original vs optimized handler
func BenchmarkHandler(b *testing.B) {
// Benchmark enabled with proper timeout
tests := []struct {
name string
n int
threshold int
}{
{"3-of-5", 5, 3},
{"5-of-7", 7, 5},
{"7-of-11", 11, 7},
}
for _, tt := range tests {
b.Run(tt.name, func(b *testing.B) {
group := curve.Secp256k1{}
partyIDs := test.PartyIDs(tt.n)
pl := pool.NewPool(0)
defer pl.TearDown()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ctx := context.Background()
logger := log.NewTestLogger(log.InfoLevel)
sessionID := []byte("test-session")
cfg := protocol.DefaultConfig()
cfg.ProtocolTimeout = 30 * time.Second // Shorter timeout for tests
handlers := make([]*protocol.Handler, tt.n)
for j, id := range partyIDs {
h, err := protocol.NewHandler(ctx, logger, metric.NewRegistry(),
lss.Keygen(group, id, partyIDs, tt.threshold, pl), sessionID, cfg)
require.NoError(b, err)
handlers[j] = h
}
runProtocol(handlers, partyIDs)
// Verify results
for _, h := range handlers {
result, err := h.Result()
require.NoError(b, err)
require.IsType(b, &config.Config{}, result)
}
}
})
}
}
// BenchmarkConcurrentMessages tests concurrent message handling
func BenchmarkConcurrentMessages(b *testing.B) {
b.Skip("Skipping benchmark that times out")
tests := []struct {
name string
n int
threshold int
parallel bool
}{
{"sequential-5", 5, 3, false},
{"parallel-5", 5, 3, true},
{"sequential-7", 7, 5, false},
{"parallel-7", 7, 5, true},
{"sequential-11", 11, 7, false},
{"parallel-11", 11, 7, true},
}
for _, tt := range tests {
b.Run(tt.name, func(b *testing.B) {
group := curve.Secp256k1{}
partyIDs := test.PartyIDs(tt.n)
pl := pool.NewPool(0)
defer pl.TearDown()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ctx := context.Background()
logger := log.NewTestLogger(log.InfoLevel)
sessionID := []byte("test-session")
cfg := protocol.DefaultConfig()
cfg.ProtocolTimeout = 30 * time.Second // Shorter timeout for tests
handlers := make([]*protocol.Handler, tt.n)
for j, id := range partyIDs {
h, err := protocol.NewHandler(ctx, logger, metric.NewRegistry(),
lss.Keygen(group, id, partyIDs, tt.threshold, pl), sessionID, cfg)
require.NoError(b, err)
handlers[j] = h
}
if tt.parallel {
runProtocolParallel(handlers, partyIDs)
} else {
runProtocol(handlers, partyIDs)
}
// Verify results
for _, h := range handlers {
result, err := h.Result()
require.NoError(b, err)
require.IsType(b, &config.Config{}, result)
}
}
})
}
}
// runProtocol runs the protocol using sequential message delivery
func runProtocol(handlers []*protocol.Handler, partyIDs []party.ID) {
network := test.NewNetwork(partyIDs)
var wg sync.WaitGroup
for i, h := range handlers {
wg.Add(1)
go func(id party.ID, handler *protocol.Handler) {
defer wg.Done()
test.HandlerLoop(id, handler, network)
}(partyIDs[i], h)
}
// Wait with timeout
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
// Protocol completed
case <-time.After(30 * time.Second):
// Timeout - protocol didn't complete
fmt.Println("Protocol timed out in benchmark")
}
}
// runProtocolParallel runs the protocol using parallel message delivery
func runProtocolParallel(handlers []*protocol.Handler, partyIDs []party.ID) {
// For now, same as sequential since the new handler already optimizes internally
runProtocol(handlers, partyIDs)
}
// BenchmarkMessageProcessing tests message processing throughput
func BenchmarkMessageProcessing(b *testing.B) {
tests := []struct {
name string
messages int
workers int
}{
{"100msgs-1worker", 100, 1},
{"100msgs-4workers", 100, 4},
{"100msgs-8workers", 100, 8},
{"1000msgs-1worker", 1000, 1},
{"1000msgs-4workers", 1000, 4},
{"1000msgs-8workers", 1000, 8},
{"10000msgs-4workers", 10000, 4},
{"10000msgs-8workers", 10000, 8},
{"10000msgs-16workers", 10000, 16},
}
for _, test := range tests {
b.Run(test.name, func(b *testing.B) {
benchmarkMessageThroughput(b, test.messages, test.workers)
})
}
}
func benchmarkMessageThroughput(b *testing.B, numMessages, numWorkers int) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Simulate message processing with worker pool
msgChan := make(chan int, numMessages)
doneChan := make(chan struct{})
var wg sync.WaitGroup
wg.Add(numWorkers)
// Start workers
for w := 0; w < numWorkers; w++ {
go func() {
defer wg.Done()
for range msgChan {
// Simulate message processing
time.Sleep(100 * time.Nanosecond)
}
}()
}
// Send messages
go func() {
for m := 0; m < numMessages; m++ {
msgChan <- m
}
close(msgChan)
}()
// Wait for completion
go func() {
wg.Wait()
close(doneChan)
}()
<-doneChan
}
}
// BenchmarkMemoryUsage measures memory allocation patterns
func BenchmarkMemoryUsage(b *testing.B) {
b.Skip("Skipping benchmark that times out")
tests := []struct {
name string
n int
threshold int
}{
{"small-3-of-5", 5, 3},
{"medium-5-of-7", 7, 5},
{"large-7-of-11", 11, 7},
}
for _, tt := range tests {
b.Run(tt.name, func(b *testing.B) {
group := curve.Secp256k1{}
partyIDs := test.PartyIDs(tt.n)
pl := pool.NewPool(0)
defer pl.TearDown()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
ctx := context.Background()
logger := log.NewTestLogger(log.InfoLevel)
sessionID := []byte("test-session")
cfg := protocol.DefaultConfig()
cfg.ProtocolTimeout = 30 * time.Second // Shorter timeout for tests
handlers := make([]*protocol.Handler, tt.n)
for j, id := range partyIDs {
h, err := protocol.NewHandler(ctx, logger, metric.NewRegistry(),
lss.Keygen(group, id, partyIDs, tt.threshold, pl), sessionID, cfg)
require.NoError(b, err)
handlers[j] = h
}
runProtocol(handlers, partyIDs)
// Force GC to measure actual memory usage
runtime.GC()
}
})
}
}
// TestHandlerPerformance provides performance comparison
func TestHandlerPerformance(t *testing.T) {
t.Skip("Skipping performance test that times out")
if testing.Short() {
t.Skip("Skipping performance test in short mode")
}
fmt.Println("\n=== Handler Performance Comparison ===")
// Test configuration
n := 7
threshold := 4
group := curve.Secp256k1{}
partyIDs := test.PartyIDs(n)
pl := pool.NewPool(0)
defer pl.TearDown()
ctx := context.Background()
logger := log.NewTestLogger(log.InfoLevel)
sessionID := []byte("test-session")
cfg := protocol.DefaultConfig()
// Measure optimized handler
start := time.Now()
origHandlers := make([]*protocol.Handler, n)
for i, id := range partyIDs {
h, err := protocol.NewHandler(ctx, logger, metric.NewRegistry(), lss.Keygen(group, id, partyIDs, threshold, pl), sessionID, cfg)
require.NoError(t, err)
origHandlers[i] = h
}
runProtocol(origHandlers, partyIDs)
origDuration := time.Since(start)
// Verify results
for _, h := range origHandlers {
result, err := h.Result()
require.NoError(t, err)
require.IsType(t, &config.Config{}, result)
}
// Measure concurrent handler (using concurrent delivery)
start = time.Now()
optHandlers := make([]*protocol.Handler, n)
for i, id := range partyIDs {
h, err := protocol.NewHandler(ctx, logger, metric.NewRegistry(), lss.Keygen(group, id, partyIDs, threshold, pl), sessionID, cfg)
require.NoError(t, err)
optHandlers[i] = h
}
runProtocolParallel(optHandlers, partyIDs)
optDuration := time.Since(start)
// Verify concurrent results
for _, h := range optHandlers {
result, err := h.Result()
require.NoError(t, err)
require.IsType(t, &config.Config{}, result)
}
// Report results
fmt.Printf("Sequential: %v\n", origDuration)
fmt.Printf("Parallel: %v\n", optDuration)
fmt.Printf("Speedup: %.2fx\n", float64(origDuration)/float64(optDuration))
// Memory comparison
var m1, m2 runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&m1)
// Run protocol again to measure memory
handlers := make([]*protocol.Handler, n)
for i, id := range partyIDs {
h, err := protocol.NewHandler(ctx, logger, metric.NewRegistry(), lss.Keygen(group, id, partyIDs, threshold, pl), sessionID, cfg)
require.NoError(t, err)
handlers[i] = h
}
runProtocol(handlers, partyIDs)
runtime.GC()
runtime.ReadMemStats(&m2)
fmt.Printf("\nMemory Usage:\n")
fmt.Printf("Alloc: %v KB\n", (m2.Alloc-m1.Alloc)/1024)
fmt.Printf("TotalAlloc: %v KB\n", (m2.TotalAlloc-m1.TotalAlloc)/1024)
fmt.Printf("NumGC: %v\n", m2.NumGC-m1.NumGC)
}