package internal import ( "context" "encoding/json" "fmt" "os" "path/filepath" "runtime" "sort" "sync" "sync/atomic" "time" "github.com/fraktal/mev-beta/pkg/math" ) type PerformanceAuditConfig struct { TestType string Duration time.Duration OutputDir string Verbose bool Concurrent int LoadLevel string ProfileEnabled bool BenchmarkMode bool StressTest bool TargetTPS int MaxMemoryMB int CPUThreshold float64 } type PerformanceAuditor struct { config *PerformanceAuditConfig results *PerformanceResults mu sync.RWMutex } type PerformanceResults struct { TestType string `json:"test_type"` Duration int64 `json:"duration_ms"` LoadLevel string `json:"load_level"` OverallScore float64 `json:"overall_score"` ThroughputResults *ThroughputResults `json:"throughput_results,omitempty"` LatencyResults *LatencyResults `json:"latency_results,omitempty"` MemoryResults *MemoryResults `json:"memory_results,omitempty"` CPUResults *CPUResults `json:"cpu_results,omitempty"` StressResults *StressResults `json:"stress_results,omitempty"` BenchmarkResults *BenchmarkResults `json:"benchmark_results,omitempty"` SystemMetrics *SystemMetrics `json:"system_metrics"` PerformanceIssues []PerformanceIssue `json:"performance_issues"` Recommendations []string `json:"recommendations"` Timestamp time.Time `json:"timestamp"` Environment EnvironmentInfo `json:"environment"` } type ThroughputResults struct { ActualTPS float64 `json:"actual_tps"` TargetTPS float64 `json:"target_tps"` MaxTPS float64 `json:"max_tps"` AverageTPS float64 `json:"average_tps"` TotalTransactions int64 `json:"total_transactions"` SuccessfulTransactions int64 `json:"successful_transactions"` FailedTransactions int64 `json:"failed_transactions"` SuccessRate float64 `json:"success_rate"` ThroughputOverTime []TPSMeasurement `json:"throughput_over_time"` } type TPSMeasurement struct { Timestamp time.Time `json:"timestamp"` TPS float64 `json:"tps"` } type LatencyResults struct { AverageLatency float64 `json:"average_latency_ms"` MedianLatency float64 `json:"median_latency_ms"` P95Latency float64 `json:"p95_latency_ms"` P99Latency float64 `json:"p99_latency_ms"` MaxLatency float64 `json:"max_latency_ms"` MinLatency float64 `json:"min_latency_ms"` LatencyDistribution map[string]int `json:"latency_distribution"` LatencyOverTime []LatencyMeasurement `json:"latency_over_time"` } type LatencyMeasurement struct { Timestamp time.Time `json:"timestamp"` Latency float64 `json:"latency_ms"` } type MemoryResults struct { MaxMemoryUsage int64 `json:"max_memory_usage_mb"` AverageMemoryUsage int64 `json:"average_memory_usage_mb"` MemoryLeakDetected bool `json:"memory_leak_detected"` GCFrequency float64 `json:"gc_frequency_per_sec"` GCPauseTime float64 `json:"gc_pause_time_ms"` HeapSize int64 `json:"heap_size_mb"` StackSize int64 `json:"stack_size_mb"` MemoryOverTime []MemoryMeasurement `json:"memory_over_time"` } type MemoryMeasurement struct { Timestamp time.Time `json:"timestamp"` HeapMB int64 `json:"heap_mb"` StackMB int64 `json:"stack_mb"` TotalMB int64 `json:"total_mb"` Goroutines int `json:"goroutines"` } type CPUResults struct { MaxCPUUsage float64 `json:"max_cpu_usage_percent"` AverageCPUUsage float64 `json:"average_cpu_usage_percent"` CPUCores int `json:"cpu_cores"` CPUEfficiency float64 `json:"cpu_efficiency_percent"` CPUOverTime []CPUMeasurement `json:"cpu_over_time"` } type CPUMeasurement struct { Timestamp time.Time `json:"timestamp"` Usage float64 `json:"usage_percent"` UserTime float64 `json:"user_time_percent"` SystemTime float64 `json:"system_time_percent"` } type StressResults struct { StressLevel string `json:"stress_level"` BreakingPoint float64 `json:"breaking_point_tps"` RecoveryTime float64 `json:"recovery_time_ms"` ErrorRate float64 `json:"error_rate_percent"` StressScenarios []StressScenario `json:"stress_scenarios"` } type StressScenario struct { Name string `json:"name"` LoadMultiplier float64 `json:"load_multiplier"` Duration int64 `json:"duration_ms"` Success bool `json:"success"` ErrorCount int `json:"error_count"` Timestamp time.Time `json:"timestamp"` } type BenchmarkResults struct { ArbitrageDetectionBench *BenchResult `json:"arbitrage_detection_bench"` PriceCalculationBench *BenchResult `json:"price_calculation_bench"` SwapSimulationBench *BenchResult `json:"swap_simulation_bench"` MemoryAllocationBench *BenchResult `json:"memory_allocation_bench"` } type BenchResult struct { Name string `json:"name"` Iterations int64 `json:"iterations"` NsPerOp int64 `json:"ns_per_op"` MBPerSec float64 `json:"mb_per_sec"` AllocsPerOp int64 `json:"allocs_per_op"` BytesPerOp int64 `json:"bytes_per_op"` } type SystemMetrics struct { StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` TotalGoroutines int `json:"total_goroutines"` MaxGoroutines int `json:"max_goroutines"` AvgGoroutines float64 `json:"avg_goroutines"` GCCycles int64 `json:"gc_cycles"` TotalAllocations int64 `json:"total_allocations_mb"` LiveObjects int64 `json:"live_objects"` } type PerformanceIssue struct { Severity string `json:"severity"` Category string `json:"category"` Description string `json:"description"` Impact string `json:"impact"` Suggestion string `json:"suggestion"` Timestamp time.Time `json:"timestamp"` } type EnvironmentInfo struct { GOOS string `json:"goos"` GOARCH string `json:"goarch"` GoVersion string `json:"go_version"` NumCPU int `json:"num_cpu"` GOMAXPROCS int `json:"gomaxprocs"` CGOEnabled bool `json:"cgo_enabled"` } func NewPerformanceAuditor(config *PerformanceAuditConfig) (*PerformanceAuditor, error) { return &PerformanceAuditor{ config: config, results: &PerformanceResults{ TestType: config.TestType, LoadLevel: config.LoadLevel, PerformanceIssues: make([]PerformanceIssue, 0), Recommendations: make([]string, 0), Timestamp: time.Now(), Environment: EnvironmentInfo{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, GoVersion: runtime.Version(), NumCPU: runtime.NumCPU(), GOMAXPROCS: runtime.GOMAXPROCS(-1), CGOEnabled: runtime.GOEXPERIMENT == "cgocheck2", }, }, }, nil } func (pa *PerformanceAuditor) RunPerformanceTests(ctx context.Context) error { startTime := time.Now() defer func() { pa.results.Duration = time.Since(startTime).Milliseconds() }() pa.results.SystemMetrics = &SystemMetrics{ StartTime: startTime, } switch pa.config.TestType { case "throughput": return pa.runThroughputTest(ctx) case "latency": return pa.runLatencyTest(ctx) case "memory": return pa.runMemoryTest(ctx) case "cpu": return pa.runCPUTest(ctx) case "stress": return pa.runStressTest(ctx) case "benchmark": return pa.runBenchmarkTest(ctx) case "all": return pa.runAllTests(ctx) default: return fmt.Errorf("unsupported test type: %s", pa.config.TestType) } } func (pa *PerformanceAuditor) runAllTests(ctx context.Context) error { tests := []struct { name string fn func(context.Context) error }{ {"throughput", pa.runThroughputTest}, {"latency", pa.runLatencyTest}, {"memory", pa.runMemoryTest}, {"cpu", pa.runCPUTest}, } if pa.config.StressTest { tests = append(tests, struct { name string fn func(context.Context) error }{"stress", pa.runStressTest}) } if pa.config.BenchmarkMode { tests = append(tests, struct { name string fn func(context.Context) error }{"benchmark", pa.runBenchmarkTest}) } for _, test := range tests { if pa.config.Verbose { fmt.Printf("Running %s test...\n", test.name) } if err := test.fn(ctx); err != nil { return fmt.Errorf("failed %s test: %w", test.name, err) } } pa.calculateOverallScore() pa.generateRecommendations() return nil } func (pa *PerformanceAuditor) runThroughputTest(ctx context.Context) error { if pa.config.Verbose { fmt.Println("Starting throughput test...") } pa.results.ThroughputResults = &ThroughputResults{ TargetTPS: float64(pa.config.TargetTPS), ThroughputOverTime: make([]TPSMeasurement, 0), } var totalTransactions int64 var successfulTransactions int64 var failedTransactions int64 // Create a ticker for measuring TPS over time ticker := time.NewTicker(time.Second) defer ticker.Stop() // Create workers for generating load var wg sync.WaitGroup transactionChan := make(chan bool, pa.config.Concurrent*10) // Start workers for i := 0; i < pa.config.Concurrent; i++ { wg.Add(1) go func() { defer wg.Done() pa.throughputWorker(ctx, transactionChan, &totalTransactions, &successfulTransactions, &failedTransactions) }() } // Monitoring goroutine monitorCtx, cancel := context.WithCancel(ctx) defer cancel() go pa.monitorThroughput(monitorCtx, &totalTransactions, ticker) // Generate load loadGenerator := time.NewTicker(time.Duration(1000000000/pa.config.TargetTPS) * time.Nanosecond) defer loadGenerator.Stop() testDuration := pa.config.Duration timeout := time.After(testDuration) LoadLoop: for { select { case <-ctx.Done(): break LoadLoop case <-timeout: break LoadLoop case <-loadGenerator.C: select { case transactionChan <- true: default: // Channel full, count as failed atomic.AddInt64(&failedTransactions, 1) } } } close(transactionChan) wg.Wait() // Calculate final results totalTxns := atomic.LoadInt64(&totalTransactions) successTxns := atomic.LoadInt64(&successfulTransactions) failedTxns := atomic.LoadInt64(&failedTransactions) pa.results.ThroughputResults.TotalTransactions = totalTxns pa.results.ThroughputResults.SuccessfulTransactions = successTxns pa.results.ThroughputResults.FailedTransactions = failedTxns if totalTxns > 0 { pa.results.ThroughputResults.SuccessRate = float64(successTxns) / float64(totalTxns) * 100.0 } durationSeconds := testDuration.Seconds() pa.results.ThroughputResults.ActualTPS = float64(totalTxns) / durationSeconds pa.results.ThroughputResults.AverageTPS = pa.results.ThroughputResults.ActualTPS // Analyze throughput performance pa.analyzeThroughputPerformance() return nil } func (pa *PerformanceAuditor) throughputWorker(ctx context.Context, transactionChan <-chan bool, totalTxns, successTxns, failedTxns *int64) { // Initialize arbitrage calculator for realistic work calculator := math.NewArbitrageCalculator() for { select { case <-ctx.Done(): return case _, ok := <-transactionChan: if !ok { return } atomic.AddInt64(totalTxns, 1) // Simulate realistic arbitrage calculation work if pa.simulateArbitrageCalculation(calculator) { atomic.AddInt64(successTxns, 1) } else { atomic.AddInt64(failedTxns, 1) } } } } func (pa *PerformanceAuditor) simulateArbitrageCalculation(calculator *math.ArbitrageCalculator) bool { // Simulate realistic arbitrage calculation // This would normally involve actual price fetching and calculation // Simple CPU-intensive operation to simulate work sum := 0.0 for i := 0; i < 1000; i++ { sum += float64(i) * 1.001 } // Simulate success/failure rate based on load level switch pa.config.LoadLevel { case "light": return sum > 0 // Almost always succeeds case "normal": return sum > 100 // Usually succeeds case "heavy": return sum > 200 // Sometimes fails case "extreme": return sum > 400 // Often fails default: return sum > 0 } } func (pa *PerformanceAuditor) monitorThroughput(ctx context.Context, totalTransactions *int64, ticker *time.Ticker) { lastCount := int64(0) for { select { case <-ctx.Done(): return case now := <-ticker.C: currentCount := atomic.LoadInt64(totalTransactions) tps := float64(currentCount - lastCount) pa.mu.Lock() pa.results.ThroughputResults.ThroughputOverTime = append( pa.results.ThroughputResults.ThroughputOverTime, TPSMeasurement{ Timestamp: now, TPS: tps, }, ) // Update max TPS if tps > pa.results.ThroughputResults.MaxTPS { pa.results.ThroughputResults.MaxTPS = tps } pa.mu.Unlock() lastCount = currentCount } } } func (pa *PerformanceAuditor) runLatencyTest(ctx context.Context) error { if pa.config.Verbose { fmt.Println("Starting latency test...") } pa.results.LatencyResults = &LatencyResults{ LatencyDistribution: make(map[string]int), LatencyOverTime: make([]LatencyMeasurement, 0), } latencies := make([]float64, 0, 10000) var mu sync.Mutex // Run latency measurements var wg sync.WaitGroup requests := 1000 // Number of requests per worker for i := 0; i < pa.config.Concurrent; i++ { wg.Add(1) go func() { defer wg.Done() pa.latencyWorker(ctx, requests, &latencies, &mu) }() } wg.Wait() // Calculate latency statistics if len(latencies) > 0 { sort.Float64s(latencies) pa.results.LatencyResults.MinLatency = latencies[0] pa.results.LatencyResults.MaxLatency = latencies[len(latencies)-1] // Calculate percentiles pa.results.LatencyResults.MedianLatency = pa.percentile(latencies, 50) pa.results.LatencyResults.P95Latency = pa.percentile(latencies, 95) pa.results.LatencyResults.P99Latency = pa.percentile(latencies, 99) // Calculate average sum := 0.0 for _, lat := range latencies { sum += lat } pa.results.LatencyResults.AverageLatency = sum / float64(len(latencies)) // Create distribution pa.createLatencyDistribution(latencies) } pa.analyzeLatencyPerformance() return nil } func (pa *PerformanceAuditor) latencyWorker(ctx context.Context, requests int, latencies *[]float64, mu *sync.Mutex) { calculator := math.NewArbitrageCalculator() for i := 0; i < requests; i++ { select { case <-ctx.Done(): return default: start := time.Now() pa.simulateArbitrageCalculation(calculator) latency := float64(time.Since(start).Nanoseconds()) / 1000000.0 // Convert to ms mu.Lock() *latencies = append(*latencies, latency) // Record over time (sample every 10th measurement) if i%10 == 0 { pa.results.LatencyResults.LatencyOverTime = append( pa.results.LatencyResults.LatencyOverTime, LatencyMeasurement{ Timestamp: time.Now(), Latency: latency, }, ) } mu.Unlock() } } } func (pa *PerformanceAuditor) percentile(sortedData []float64, percentile int) float64 { if len(sortedData) == 0 { return 0 } index := int(float64(len(sortedData)) * float64(percentile) / 100.0) if index >= len(sortedData) { index = len(sortedData) - 1 } return sortedData[index] } func (pa *PerformanceAuditor) createLatencyDistribution(latencies []float64) { // Create distribution buckets buckets := map[string]int{ "< 1ms": 0, "1-5ms": 0, "5-10ms": 0, "10-50ms": 0, "50-100ms": 0, "> 100ms": 0, } for _, lat := range latencies { switch { case lat < 1: buckets["< 1ms"]++ case lat < 5: buckets["1-5ms"]++ case lat < 10: buckets["5-10ms"]++ case lat < 50: buckets["10-50ms"]++ case lat < 100: buckets["50-100ms"]++ default: buckets["> 100ms"]++ } } pa.results.LatencyResults.LatencyDistribution = buckets } func (pa *PerformanceAuditor) runMemoryTest(ctx context.Context) error { if pa.config.Verbose { fmt.Println("Starting memory test...") } pa.results.MemoryResults = &MemoryResults{ MemoryOverTime: make([]MemoryMeasurement, 0), } // Monitor memory usage ticker := time.NewTicker(time.Second) defer ticker.Stop() monitorCtx, cancel := context.WithCancel(ctx) defer cancel() go pa.monitorMemoryUsage(monitorCtx, ticker) // Generate memory load return pa.generateMemoryLoad(ctx) } func (pa *PerformanceAuditor) monitorMemoryUsage(ctx context.Context, ticker *time.Ticker) { var m runtime.MemStats var maxMemory int64 var totalMemory int64 var measurements int64 for { select { case <-ctx.Done(): return case now := <-ticker.C: runtime.ReadMemStats(&m) heapMB := int64(m.HeapInuse) / 1024 / 1024 stackMB := int64(m.StackInuse) / 1024 / 1024 totalMB := heapMB + stackMB if totalMB > maxMemory { maxMemory = totalMB } totalMemory += totalMB measurements++ pa.mu.Lock() pa.results.MemoryResults.MemoryOverTime = append( pa.results.MemoryResults.MemoryOverTime, MemoryMeasurement{ Timestamp: now, HeapMB: heapMB, StackMB: stackMB, TotalMB: totalMB, Goroutines: runtime.NumGoroutine(), }, ) pa.results.MemoryResults.MaxMemoryUsage = maxMemory if measurements > 0 { pa.results.MemoryResults.AverageMemoryUsage = totalMemory / measurements } pa.results.MemoryResults.HeapSize = heapMB pa.results.MemoryResults.StackSize = stackMB pa.mu.Unlock() } } } func (pa *PerformanceAuditor) generateMemoryLoad(ctx context.Context) error { // Generate memory pressure by creating and releasing large data structures var wg sync.WaitGroup for i := 0; i < pa.config.Concurrent; i++ { wg.Add(1) go func() { defer wg.Done() pa.memoryWorker(ctx) }() } wg.Wait() // Detect memory leaks pa.detectMemoryLeaks() pa.analyzeMemoryPerformance() return nil } func (pa *PerformanceAuditor) memoryWorker(ctx context.Context) { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: // Allocate memory to simulate real workload data := make([]byte, 1024*1024) // 1MB // Do some work with the data for i := range data { data[i] = byte(i % 256) } // Simulate processing time time.Sleep(10 * time.Millisecond) // Allow garbage collection data = nil } } } func (pa *PerformanceAuditor) detectMemoryLeaks() { if len(pa.results.MemoryResults.MemoryOverTime) < 10 { return } // Simple memory leak detection: check if memory usage is consistently increasing measurements := pa.results.MemoryResults.MemoryOverTime start := measurements[0].TotalMB end := measurements[len(measurements)-1].TotalMB // If memory usage increased by more than 50% and didn't decrease significantly if float64(end) > float64(start)*1.5 { pa.results.MemoryResults.MemoryLeakDetected = true pa.addPerformanceIssue("CRITICAL", "memory", "Potential memory leak detected: memory usage increased significantly during test", "High memory usage may lead to system instability", "Review memory allocation patterns and ensure proper cleanup") } } func (pa *PerformanceAuditor) runCPUTest(ctx context.Context) error { if pa.config.Verbose { fmt.Println("Starting CPU test...") } pa.results.CPUResults = &CPUResults{ CPUCores: runtime.NumCPU(), CPUOverTime: make([]CPUMeasurement, 0), } // Generate CPU load and monitor return pa.generateCPULoad(ctx) } func (pa *PerformanceAuditor) generateCPULoad(ctx context.Context) error { var wg sync.WaitGroup // Start CPU workers for i := 0; i < pa.config.Concurrent; i++ { wg.Add(1) go func() { defer wg.Done() pa.cpuWorker(ctx) }() } // Monitor CPU usage go pa.monitorCPUUsage(ctx) wg.Wait() pa.analyzeCPUPerformance() return nil } func (pa *PerformanceAuditor) cpuWorker(ctx context.Context) { // CPU-intensive work calculator := math.NewArbitrageCalculator() for { select { case <-ctx.Done(): return default: // Simulate complex arbitrage calculations for i := 0; i < 10000; i++ { pa.simulateArbitrageCalculation(calculator) } } } } func (pa *PerformanceAuditor) monitorCPUUsage(ctx context.Context) { ticker := time.NewTicker(time.Second) defer ticker.Stop() // Simple CPU monitoring (in real implementation, would use more sophisticated methods) var maxCPU float64 var totalCPU float64 var measurements int for { select { case <-ctx.Done(): return case now := <-ticker.C: // Simulate CPU usage measurement (simplified) cpuUsage := float64(runtime.NumGoroutine()) / float64(runtime.NumCPU()) * 10.0 if cpuUsage > 100.0 { cpuUsage = 100.0 } if cpuUsage > maxCPU { maxCPU = cpuUsage } totalCPU += cpuUsage measurements++ pa.mu.Lock() pa.results.CPUResults.CPUOverTime = append( pa.results.CPUResults.CPUOverTime, CPUMeasurement{ Timestamp: now, Usage: cpuUsage, UserTime: cpuUsage * 0.8, // Simplified SystemTime: cpuUsage * 0.2, // Simplified }, ) pa.results.CPUResults.MaxCPUUsage = maxCPU if measurements > 0 { pa.results.CPUResults.AverageCPUUsage = totalCPU / float64(measurements) } pa.mu.Unlock() } } } func (pa *PerformanceAuditor) runStressTest(ctx context.Context) error { if pa.config.Verbose { fmt.Println("Starting stress test...") } pa.results.StressResults = &StressResults{ StressLevel: pa.config.LoadLevel, StressScenarios: make([]StressScenario, 0), } // Define stress scenarios scenarios := []struct { name string loadMultiplier float64 duration time.Duration }{ {"baseline", 1.0, 30 * time.Second}, {"moderate_stress", 2.0, 30 * time.Second}, {"high_stress", 5.0, 30 * time.Second}, {"extreme_stress", 10.0, 30 * time.Second}, {"breaking_point", 20.0, 30 * time.Second}, } for _, scenario := range scenarios { if pa.config.Verbose { fmt.Printf("Running stress scenario: %s\n", scenario.name) } result := pa.runStressScenario(ctx, scenario.name, scenario.loadMultiplier, scenario.duration) pa.results.StressResults.StressScenarios = append(pa.results.StressResults.StressScenarios, result) // If we found the breaking point, record it if !result.Success && pa.results.StressResults.BreakingPoint == 0 { pa.results.StressResults.BreakingPoint = scenario.loadMultiplier * float64(pa.config.TargetTPS) } } pa.analyzeStressPerformance() return nil } func (pa *PerformanceAuditor) runStressScenario(ctx context.Context, name string, loadMultiplier float64, duration time.Duration) StressScenario { scenario := StressScenario{ Name: name, LoadMultiplier: loadMultiplier, Duration: duration.Milliseconds(), Timestamp: time.Now(), } // Create scenario-specific context scenarioCtx, cancel := context.WithTimeout(ctx, duration) defer cancel() // Increase load based on multiplier targetTPS := int(float64(pa.config.TargetTPS) * loadMultiplier) concurrent := int(float64(pa.config.Concurrent) * loadMultiplier) var errorCount int64 var successCount int64 var wg sync.WaitGroup // Start workers for i := 0; i < concurrent; i++ { wg.Add(1) go func() { defer wg.Done() pa.stressWorker(scenarioCtx, &successCount, &errorCount) }() } wg.Wait() total := errorCount + successCount scenario.ErrorCount = int(errorCount) scenario.Success = total > 0 && float64(errorCount)/float64(total) < 0.1 // Less than 10% errors return scenario } func (pa *PerformanceAuditor) stressWorker(ctx context.Context, successCount, errorCount *int64) { calculator := math.NewArbitrageCalculator() for { select { case <-ctx.Done(): return default: if pa.simulateArbitrageCalculation(calculator) { atomic.AddInt64(successCount, 1) } else { atomic.AddInt64(errorCount, 1) } } } } func (pa *PerformanceAuditor) runBenchmarkTest(ctx context.Context) error { if pa.config.Verbose { fmt.Println("Starting benchmark test...") } pa.results.BenchmarkResults = &BenchmarkResults{} // Run different benchmark scenarios benchmarks := []struct { name string fn func() *BenchResult }{ {"arbitrage_detection", pa.benchmarkArbitrageDetection}, {"price_calculation", pa.benchmarkPriceCalculation}, {"swap_simulation", pa.benchmarkSwapSimulation}, {"memory_allocation", pa.benchmarkMemoryAllocation}, } for _, benchmark := range benchmarks { if pa.config.Verbose { fmt.Printf("Running benchmark: %s\n", benchmark.name) } result := benchmark.fn() switch benchmark.name { case "arbitrage_detection": pa.results.BenchmarkResults.ArbitrageDetectionBench = result case "price_calculation": pa.results.BenchmarkResults.PriceCalculationBench = result case "swap_simulation": pa.results.BenchmarkResults.SwapSimulationBench = result case "memory_allocation": pa.results.BenchmarkResults.MemoryAllocationBench = result } } pa.analyzeBenchmarkPerformance() return nil } func (pa *PerformanceAuditor) benchmarkArbitrageDetection() *BenchResult { iterations := int64(1000) start := time.Now() var allocs1, allocs2 runtime.MemStats runtime.GC() runtime.ReadMemStats(&allocs1) calculator := math.NewArbitrageCalculator() for i := int64(0); i < iterations; i++ { pa.simulateArbitrageCalculation(calculator) } runtime.ReadMemStats(&allocs2) duration := time.Since(start) return &BenchResult{ Name: "arbitrage_detection", Iterations: iterations, NsPerOp: duration.Nanoseconds() / iterations, AllocsPerOp: int64(allocs2.Mallocs-allocs1.Mallocs) / iterations, BytesPerOp: int64(allocs2.TotalAlloc-allocs1.TotalAlloc) / iterations, } } func (pa *PerformanceAuditor) benchmarkPriceCalculation() *BenchResult { iterations := int64(5000) start := time.Now() // Simulate price calculations for i := int64(0); i < iterations; i++ { _ = float64(i) * 1.001 * 2000.0 / 1.0005 } duration := time.Since(start) return &BenchResult{ Name: "price_calculation", Iterations: iterations, NsPerOp: duration.Nanoseconds() / iterations, } } func (pa *PerformanceAuditor) benchmarkSwapSimulation() *BenchResult { iterations := int64(2000) start := time.Now() // Simulate swap calculations for i := int64(0); i < iterations; i++ { // Simplified swap simulation amount := float64(i + 1) reserve1 := 1000000.0 reserve2 := 2000000.0 _ = amount * reserve2 / (reserve1 + amount) } duration := time.Since(start) return &BenchResult{ Name: "swap_simulation", Iterations: iterations, NsPerOp: duration.Nanoseconds() / iterations, } } func (pa *PerformanceAuditor) benchmarkMemoryAllocation() *BenchResult { iterations := int64(1000) start := time.Now() var allocs1, allocs2 runtime.MemStats runtime.GC() runtime.ReadMemStats(&allocs1) for i := int64(0); i < iterations; i++ { data := make([]byte, 1024) _ = data } runtime.ReadMemStats(&allocs2) duration := time.Since(start) return &BenchResult{ Name: "memory_allocation", Iterations: iterations, NsPerOp: duration.Nanoseconds() / iterations, AllocsPerOp: int64(allocs2.Mallocs-allocs1.Mallocs) / iterations, BytesPerOp: int64(allocs2.TotalAlloc-allocs1.TotalAlloc) / iterations, } } func (pa *PerformanceAuditor) calculateOverallScore() { scores := make([]float64, 0) // Throughput score if pa.results.ThroughputResults != nil { score := (pa.results.ThroughputResults.ActualTPS / pa.results.ThroughputResults.TargetTPS) * 100.0 if score > 100.0 { score = 100.0 } scores = append(scores, score) } // Latency score (lower is better, convert to score) if pa.results.LatencyResults != nil { // Good latency is < 10ms, acceptable is < 50ms latencyScore := 100.0 if pa.results.LatencyResults.P95Latency > 50.0 { latencyScore = 50.0 } else if pa.results.LatencyResults.P95Latency > 10.0 { latencyScore = 100.0 - (pa.results.LatencyResults.P95Latency-10.0)*1.25 } scores = append(scores, latencyScore) } // Memory score if pa.results.MemoryResults != nil { memoryScore := 100.0 if pa.results.MemoryResults.MemoryLeakDetected { memoryScore -= 30.0 } if pa.results.MemoryResults.MaxMemoryUsage > int64(pa.config.MaxMemoryMB) { memoryScore -= 20.0 } scores = append(scores, memoryScore) } // CPU score if pa.results.CPUResults != nil { cpuScore := 100.0 if pa.results.CPUResults.MaxCPUUsage > pa.config.CPUThreshold { cpuScore = 100.0 - (pa.results.CPUResults.MaxCPUUsage-pa.config.CPUThreshold)*2.0 } if cpuScore < 0 { cpuScore = 0 } scores = append(scores, cpuScore) } // Calculate overall score if len(scores) > 0 { total := 0.0 for _, score := range scores { total += score } pa.results.OverallScore = total / float64(len(scores)) } } func (pa *PerformanceAuditor) analyzeThroughputPerformance() { if pa.results.ThroughputResults == nil { return } tr := pa.results.ThroughputResults if tr.ActualTPS < tr.TargetTPS*0.8 { pa.addPerformanceIssue("HIGH", "throughput", fmt.Sprintf("Throughput %.1f TPS is significantly below target %.1f TPS", tr.ActualTPS, tr.TargetTPS), "Reduced system capacity and potential revenue loss", "Optimize bottlenecks in transaction processing pipeline") } if tr.SuccessRate < 95.0 { pa.addPerformanceIssue("HIGH", "reliability", fmt.Sprintf("Transaction success rate %.1f%% is below acceptable threshold", tr.SuccessRate), "High failure rate indicates system instability", "Investigate error patterns and improve error handling") } } func (pa *PerformanceAuditor) analyzeLatencyPerformance() { if pa.results.LatencyResults == nil { return } lr := pa.results.LatencyResults if lr.P95Latency > 100.0 { pa.addPerformanceIssue("HIGH", "latency", fmt.Sprintf("P95 latency %.2f ms exceeds acceptable threshold", lr.P95Latency), "High latency reduces arbitrage opportunity capture rate", "Optimize hot paths and reduce processing overhead") } if lr.P99Latency > 500.0 { pa.addPerformanceIssue("CRITICAL", "latency", fmt.Sprintf("P99 latency %.2f ms is extremely high", lr.P99Latency), "Extreme latency spikes may cause missed opportunities", "Investigate and eliminate latency spikes") } } func (pa *PerformanceAuditor) analyzeMemoryPerformance() { if pa.results.MemoryResults == nil { return } mr := pa.results.MemoryResults if mr.MaxMemoryUsage > int64(pa.config.MaxMemoryMB) { pa.addPerformanceIssue("MEDIUM", "memory", fmt.Sprintf("Memory usage %d MB exceeds configured limit %d MB", mr.MaxMemoryUsage, pa.config.MaxMemoryMB), "High memory usage may lead to system instability", "Optimize memory allocation patterns and implement memory pooling") } } func (pa *PerformanceAuditor) analyzeCPUPerformance() { if pa.results.CPUResults == nil { return } cr := pa.results.CPUResults if cr.MaxCPUUsage > pa.config.CPUThreshold { pa.addPerformanceIssue("MEDIUM", "cpu", fmt.Sprintf("CPU usage %.1f%% exceeds threshold %.1f%%", cr.MaxCPUUsage, pa.config.CPUThreshold), "High CPU usage may limit system scalability", "Optimize CPU-intensive operations and consider load balancing") } } func (pa *PerformanceAuditor) analyzeStressPerformance() { if pa.results.StressResults == nil { return } sr := pa.results.StressResults if sr.BreakingPoint > 0 && sr.BreakingPoint < float64(pa.config.TargetTPS)*2.0 { pa.addPerformanceIssue("HIGH", "scalability", fmt.Sprintf("System breaking point %.1f TPS is low", sr.BreakingPoint), "Limited scalability may restrict growth potential", "Improve system architecture for better scalability") } } func (pa *PerformanceAuditor) analyzeBenchmarkPerformance() { if pa.results.BenchmarkResults == nil { return } // Analyze benchmark results for performance insights if br := pa.results.BenchmarkResults.ArbitrageDetectionBench; br != nil { if br.NsPerOp > 1000000 { // More than 1ms per operation pa.addPerformanceIssue("MEDIUM", "performance", "Arbitrage detection benchmark shows high per-operation latency", "Slow arbitrage detection may miss time-sensitive opportunities", "Optimize arbitrage detection algorithms") } } } func (pa *PerformanceAuditor) addPerformanceIssue(severity, category, description, impact, suggestion string) { pa.mu.Lock() defer pa.mu.Unlock() pa.results.PerformanceIssues = append(pa.results.PerformanceIssues, PerformanceIssue{ Severity: severity, Category: category, Description: description, Impact: impact, Suggestion: suggestion, Timestamp: time.Now(), }) } func (pa *PerformanceAuditor) generateRecommendations() { if pa.results.OverallScore < 70.0 { pa.results.Recommendations = append(pa.results.Recommendations, "Overall performance score is below 70%. Consider comprehensive optimization.") } if pa.results.OverallScore >= 80.0 { pa.results.Recommendations = append(pa.results.Recommendations, "Good performance achieved. Focus on monitoring and gradual improvements.") } // Category-specific recommendations if pa.results.ThroughputResults != nil && pa.results.ThroughputResults.ActualTPS < pa.results.ThroughputResults.TargetTPS*0.9 { pa.results.Recommendations = append(pa.results.Recommendations, "Throughput is below target. Consider parallel processing and pipeline optimization.") } if pa.results.LatencyResults != nil && pa.results.LatencyResults.P95Latency > 50.0 { pa.results.Recommendations = append(pa.results.Recommendations, "High latency detected. Optimize critical paths and consider caching strategies.") } if pa.results.MemoryResults != nil && pa.results.MemoryResults.MemoryLeakDetected { pa.results.Recommendations = append(pa.results.Recommendations, "Memory leak detected. Review memory allocation patterns and implement proper cleanup.") } } func (pa *PerformanceAuditor) GenerateReport() error { // Update system metrics pa.results.SystemMetrics.EndTime = time.Now() pa.results.SystemMetrics.TotalGoroutines = runtime.NumGoroutine() // Sort performance issues by severity sort.Slice(pa.results.PerformanceIssues, func(i, j int) bool { severityOrder := map[string]int{"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3} return severityOrder[pa.results.PerformanceIssues[i].Severity] < severityOrder[pa.results.PerformanceIssues[j].Severity] }) // Generate JSON report jsonReport, err := json.MarshalIndent(pa.results, "", " ") if err != nil { return fmt.Errorf("failed to marshal results: %w", err) } // Save JSON report timestamp := time.Now().Format("2006-01-02_15-04-05") jsonPath := filepath.Join(pa.config.OutputDir, fmt.Sprintf("performance_audit_%s.json", timestamp)) if err := os.WriteFile(jsonPath, jsonReport, 0644); err != nil { return fmt.Errorf("failed to write JSON report: %w", err) } // Generate summary report summaryPath := filepath.Join(pa.config.OutputDir, fmt.Sprintf("performance_summary_%s.txt", timestamp)) if err := pa.generateSummaryReport(summaryPath); err != nil { return fmt.Errorf("failed to generate summary report: %w", err) } if pa.config.Verbose { fmt.Printf("Reports generated:\n") fmt.Printf(" JSON: %s\n", jsonPath) fmt.Printf(" Summary: %s\n", summaryPath) } return nil } func (pa *PerformanceAuditor) generateSummaryReport(filePath string) error { summary := fmt.Sprintf(`Performance Audit Report Generated: %s Test Type: %s Load Level: %s Duration: %d ms Overall Score: %.1f%% ENVIRONMENT =========== OS: %s Architecture: %s Go Version: %s CPU Cores: %d GOMAXPROCS: %d `, pa.results.Timestamp.Format("2006-01-02 15:04:05"), pa.results.TestType, pa.results.LoadLevel, pa.results.Duration, pa.results.OverallScore, pa.results.Environment.GOOS, pa.results.Environment.GOARCH, pa.results.Environment.GoVersion, pa.results.Environment.NumCPU, pa.results.Environment.GOMAXPROCS) // Throughput results if pa.results.ThroughputResults != nil { tr := pa.results.ThroughputResults summary += fmt.Sprintf(`THROUGHPUT RESULTS ================== Target TPS: %.1f Actual TPS: %.1f Max TPS: %.1f Success Rate: %.1f%% Total Transactions: %d `, tr.TargetTPS, tr.ActualTPS, tr.MaxTPS, tr.SuccessRate, tr.TotalTransactions) } // Latency results if pa.results.LatencyResults != nil { lr := pa.results.LatencyResults summary += fmt.Sprintf(`LATENCY RESULTS =============== Average: %.2f ms Median: %.2f ms P95: %.2f ms P99: %.2f ms Max: %.2f ms `, lr.AverageLatency, lr.MedianLatency, lr.P95Latency, lr.P99Latency, lr.MaxLatency) } // Memory results if pa.results.MemoryResults != nil { mr := pa.results.MemoryResults summary += fmt.Sprintf(`MEMORY RESULTS ============== Max Usage: %d MB Average Usage: %d MB Memory Leak: %t Heap Size: %d MB `, mr.MaxMemoryUsage, mr.AverageMemoryUsage, mr.MemoryLeakDetected, mr.HeapSize) } // CPU results if pa.results.CPUResults != nil { cr := pa.results.CPUResults summary += fmt.Sprintf(`CPU RESULTS =========== Max Usage: %.1f%% Average Usage: %.1f%% CPU Cores: %d `, cr.MaxCPUUsage, cr.AverageCPUUsage, cr.CPUCores) } // Performance issues if len(pa.results.PerformanceIssues) > 0 { summary += "\nPERFORMANCE ISSUES\n==================\n" for i, issue := range pa.results.PerformanceIssues { summary += fmt.Sprintf("%d. [%s] %s: %s\n", i+1, issue.Severity, issue.Category, issue.Description) } } // Recommendations if len(pa.results.Recommendations) > 0 { summary += "\nRECOMMENDATIONS\n===============\n" for i, rec := range pa.results.Recommendations { summary += fmt.Sprintf("%d. %s\n", i+1, rec) } } return os.WriteFile(filePath, []byte(summary), 0644) }