Performance Benchmarking #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Performance Benchmarking | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'design/**' | |
| schedule: | |
| - cron: '0 2 * * 0' # Weekly on Sunday at 2 AM UTC | |
| env: | |
| CACHE_NAME_PREFIX: perf-bench | |
| RESULTS_DIR: performance-results | |
| jobs: | |
| # ─── Gas Cost Benchmarking ────────────────────────────────────────────────── | |
| gas-benchmarks: | |
| name: Contract Gas Cost Benchmarking | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools-preview | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: ". -> target" | |
| - name: Install benchmarking tools | |
| run: | | |
| cargo install wasm-opt || true | |
| - name: Run gas cost benchmarks | |
| run: | | |
| mkdir -p ${{ env.RESULTS_DIR }} | |
| cargo test \ | |
| --manifest-path contracts/stellar-save/Cargo.toml \ | |
| --lib benchmark \ | |
| -- --nocapture \ | |
| --test-threads=1 2>&1 | tee ${{ env.RESULTS_DIR }}/gas-benchmarks.log | |
| - name: Analyze gas costs | |
| run: | | |
| python3 - <<'EOF' | |
| import json | |
| import re | |
| from datetime import datetime | |
| # Parse benchmark results | |
| benchmark_results = {} | |
| try: | |
| with open("${{ env.RESULTS_DIR }}/gas-benchmarks.log", "r") as f: | |
| content = f.read() | |
| # Extract gas costs from test output | |
| gas_pattern = r"test.*?gas.*?(\d+)" | |
| matches = re.findall(gas_pattern, content, re.IGNORECASE) | |
| if matches: | |
| benchmark_results['gas_costs'] = [int(m) for m in matches] | |
| except Exception as e: | |
| print(f"Error reading benchmarks: {e}") | |
| # Generate report | |
| report = { | |
| 'timestamp': datetime.utcnow().isoformat(), | |
| 'branch': '${{ github.ref_name }}', | |
| 'commit_sha': '${{ github.sha }}', | |
| 'results': benchmark_results | |
| } | |
| with open("${{ env.RESULTS_DIR }}/gas-report.json", "w") as f: | |
| json.dump(report, f, indent=2) | |
| print("Gas benchmarking complete") | |
| EOF | |
| - name: Check gas regression thresholds | |
| run: | | |
| python3 - <<'EOF' | |
| import json | |
| # Define thresholds for critical functions | |
| THRESHOLDS = { | |
| 'contribution': 2000000, # 2M gas | |
| 'auto_advance': 3000000, # 3M gas | |
| 'distribute_winnings': 4000000, # 4M gas | |
| 'create_group': 1500000, # 1.5M gas | |
| } | |
| results_file = "${{ env.RESULTS_DIR }}/gas-report.json" | |
| try: | |
| with open(results_file, "r") as f: | |
| report = json.load(f) | |
| costs = report.get('results', {}).get('gas_costs', []) | |
| if costs and any(c > max(THRESHOLDS.values()) for c in costs): | |
| print(f"⚠️ WARNING: Some gas costs exceed thresholds!") | |
| exit(1) | |
| else: | |
| print(f"✓ Gas costs within acceptable thresholds") | |
| except Exception as e: | |
| print(f"Error checking thresholds: {e}") | |
| EOF | |
| - name: Upload gas benchmarks | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: gas-benchmarks | |
| path: ${{ env.RESULTS_DIR }}/ | |
| # ─── Frontend Performance Benchmarking ────────────────────────────────────── | |
| frontend-performance: | |
| name: Frontend Performance Metrics | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| working-directory: frontend | |
| - name: Build frontend optimized | |
| run: npm run build | |
| working-directory: frontend | |
| env: | |
| NODE_ENV: production | |
| - name: Create Lighthouse config | |
| run: | | |
| cat > frontend/.lighthouserc-perf.json <<'EOF' | |
| { | |
| "ci": { | |
| "upload": { | |
| "target": "temporary-public-storage" | |
| }, | |
| "collect": { | |
| "url": [ | |
| "http://localhost:4173", | |
| "http://localhost:4173/groups", | |
| "http://localhost:4173/profile" | |
| ], | |
| "numberOfRuns": 3, | |
| "settings": { | |
| "configPath": "frontend/lighthouse-config.js" | |
| } | |
| }, | |
| "assert": { | |
| "preset": "lighthouse:recommended", | |
| "assertions": { | |
| "categories:performance": ["error", {"minScore": 0.85}], | |
| "categories:accessibility": ["error", {"minScore": 0.90}], | |
| "categories:best-practices": ["error", {"minScore": 0.85}], | |
| "categories:seo": ["error", {"minScore": 0.90}], | |
| "categories:pwa": ["warn"] | |
| } | |
| } | |
| } | |
| } | |
| EOF | |
| - name: Start frontend preview | |
| run: | | |
| cd frontend | |
| nohup npm run preview -- --host 127.0.0.1 --port 4173 > /tmp/lighthouse-server.log 2>&1 & | |
| echo $! > /tmp/lighthouse-server.pid | |
| sleep 5 | |
| - name: Run Lighthouse benchmarks | |
| run: | | |
| mkdir -p ${{ env.RESULTS_DIR }} | |
| cd frontend | |
| npx lhci autorun --config .lighthouserc-perf.json > ../${{ env.RESULTS_DIR }}/lighthouse.log 2>&1 || true | |
| - name: Extract and analyze metrics | |
| run: | | |
| python3 - <<'EOF' | |
| import json | |
| from datetime import datetime | |
| metrics = { | |
| 'timestamp': datetime.utcnow().isoformat(), | |
| 'branch': '${{ github.ref_name }}', | |
| 'lightouse_results': {}, | |
| 'web_vitals': {} | |
| } | |
| try: | |
| with open("${{ env.RESULTS_DIR }}/lighthouse.log", "r") as f: | |
| content = f.read() | |
| metrics['lighthouse_report'] = content[:500] | |
| except: | |
| pass | |
| with open("${{ env.RESULTS_DIR }}/performance-metrics.json", "w") as f: | |
| json.dump(metrics, f, indent=2) | |
| print("Frontend performance metrics collected") | |
| EOF | |
| - name: Stop frontend preview | |
| run: | | |
| PID=$(cat /tmp/lighthouse-server.pid 2>/dev/null) | |
| kill $PID 2>/dev/null || true | |
| - name: Upload frontend performance results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: frontend-performance | |
| path: ${{ env.RESULTS_DIR }}/ | |
| # ─── Performance Trend Tracking ───────────────────────────────────────────── | |
| performance-trends: | |
| name: Performance Trend Analysis | |
| needs: [gas-benchmarks, frontend-performance] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all performance artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifact-downloads | |
| - name: Analyze performance trends | |
| run: | | |
| mkdir -p ${{ env.RESULTS_DIR }} | |
| python3 - <<'EOF' | |
| import json | |
| import os | |
| from datetime import datetime | |
| from pathlib import Path | |
| artifacts_dir = "artifact-downloads" | |
| trends_data = { | |
| 'timestamp': datetime.utcnow().isoformat(), | |
| 'branch': '${{ github.ref_name }}', | |
| 'build_number': '${{ github.run_number }}', | |
| 'commit_sha': '${{ github.sha }}', | |
| 'gas_benchmarks': {}, | |
| 'frontend_metrics': {}, | |
| 'regression_alerts': [] | |
| } | |
| # Collect gas benchmark data | |
| gas_report_path = Path(artifacts_dir) / "gas-benchmarks" / "gas-report.json" | |
| if gas_report_path.exists(): | |
| with open(gas_report_path) as f: | |
| trends_data['gas_benchmarks'] = json.load(f) | |
| # Collect frontend performance data | |
| perf_metrics_path = Path(artifacts_dir) / "frontend-performance" / "performance-metrics.json" | |
| if perf_metrics_path.exists(): | |
| with open(perf_metrics_path) as f: | |
| trends_data['frontend_metrics'] = json.load(f) | |
| # Define regression detection thresholds | |
| regressions = [] | |
| gas_costs = trends_data['gas_benchmarks'].get('results', {}).get('gas_costs', []) | |
| if gas_costs: | |
| avg_gas = sum(gas_costs) / len(gas_costs) | |
| # Flag if average gas is 10% above baseline (2M) | |
| if avg_gas > 2200000: | |
| regressions.append(f"Gas cost regression detected: {avg_gas:.0f} gas") | |
| trends_data['regression_alerts'] = regressions | |
| # Save trends | |
| with open("${{ env.RESULTS_DIR }}/performance-trends.json", "w") as f: | |
| json.dump(trends_data, f, indent=2) | |
| if regressions: | |
| print("⚠️ Performance regressions detected:") | |
| for r in regressions: | |
| print(f" - {r}") | |
| else: | |
| print("✓ No performance regressions detected") | |
| EOF | |
| - name: Generate performance dashboard | |
| run: | | |
| cat > ${{ env.RESULTS_DIR }}/dashboard.html <<'EOF' | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Performance Dashboard</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; } | |
| .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; } | |
| h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; } | |
| .metric-card { display: inline-block; margin: 10px; padding: 15px; background: #f9f9f9; border-left: 4px solid #007bff; border-radius: 4px; } | |
| .metric-value { font-size: 24px; font-weight: bold; color: #007bff; } | |
| .metric-label { font-size: 12px; color: #666; margin-top: 5px; } | |
| .alert { padding: 10px; margin: 10px 0; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px; } | |
| .success { background: #d4edda; border-left-color: #28a745; } | |
| .error { background: #f8d7da; border-left-color: #dc3545; } | |
| table { width: 100%; border-collapse: collapse; margin-top: 20px; } | |
| th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } | |
| th { background: #f9f9f9; font-weight: bold; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🎯 Performance Dashboard</h1> | |
| <p>Generated on: <strong id="timestamp"></strong></p> | |
| <h2>Key Metrics</h2> | |
| <div class="metric-card"> | |
| <div class="metric-value" id="gas-value">N/A</div> | |
| <div class="metric-label">Avg Gas Cost</div> | |
| </div> | |
| <div class="metric-card"> | |
| <div class="metric-value" id="lighthouse-value">N/A</div> | |
| <div class="metric-label">Lighthouse Score</div> | |
| </div> | |
| <h2>Status</h2> | |
| <div id="status-container"></div> | |
| <h2>Recent Data</h2> | |
| <table id="data-table"> | |
| <tr> | |
| <th>Metric</th> | |
| <th>Value</th> | |
| <th>Status</th> | |
| </tr> | |
| </table> | |
| </div> | |
| <script> | |
| document.getElementById('timestamp').textContent = new Date().toISOString(); | |
| document.getElementById('status-container').innerHTML = '<div class="alert success">✓ Performance monitoring active</div>'; | |
| </script> | |
| </body> | |
| </html> | |
| EOF | |
| - name: Upload performance trends | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: performance-dashboard | |
| path: ${{ env.RESULTS_DIR }}/ | |
| - name: Comment on PR with performance results | |
| uses: actions/github-script@v7 | |
| if: github.event_name == 'pull_request' | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| let performanceReport = '## 📊 Performance Benchmarking Results\n\n'; | |
| try { | |
| const trendsPath = path.join('${{ env.RESULTS_DIR }}', 'performance-trends.json'); | |
| if (fs.existsSync(trendsPath)) { | |
| const trends = JSON.parse(fs.readFileSync(trendsPath, 'utf8')); | |
| if (trends.regression_alerts && trends.regression_alerts.length > 0) { | |
| performanceReport += '⚠️ **Performance Regressions Detected**\n'; | |
| trends.regression_alerts.forEach(alert => { | |
| performanceReport += `- ${alert}\n`; | |
| }); | |
| } else { | |
| performanceReport += '✅ No performance regressions detected\n'; | |
| } | |
| } | |
| } catch (e) { | |
| performanceReport += 'Could not load performance data\n'; | |
| } | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: performanceReport | |
| }); |