docs: external perf notes + verification #14
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 Gate | |
| on: | |
| pull_request: | |
| branches: [master] | |
| paths: | |
| - 'server/src/**' | |
| - 'client/src/**' | |
| - 'protocol/**' | |
| jobs: | |
| perf-benchmark: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v1 | |
| with: | |
| bun-version: latest | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Map local.hytopiahosting.com to localhost | |
| run: echo "127.0.0.1 local.hytopiahosting.com" | sudo tee -a /etc/hosts | |
| - name: Install server dependencies | |
| run: cd server && npm ci | |
| - name: Build SDK | |
| run: cd server && npm run build | |
| - name: Install perf-tools | |
| run: cd packages/perf-tools && PUPPETEER_SKIP_DOWNLOAD=true npm ci && npm run build | |
| - name: Restore baseline cache | |
| id: cache-baseline | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: .perf-baseline/ | |
| key: perf-baseline-${{ github.base_ref }} | |
| - name: Run idle benchmark | |
| run: | | |
| cd packages/perf-tools | |
| npx hytopia-bench run --preset idle --output ../../.perf-results/idle.json --verbose | |
| continue-on-error: true | |
| - name: Run stress benchmark | |
| run: | | |
| cd packages/perf-tools | |
| npx hytopia-bench run --preset stress --output ../../.perf-results/stress.json --verbose | |
| continue-on-error: true | |
| - name: Compare with baseline | |
| if: steps.cache-baseline.outputs.cache-hit == 'true' | |
| run: | | |
| cd packages/perf-tools | |
| RESULTS="" | |
| for preset in idle stress; do | |
| if [ -f "../../.perf-baseline/${preset}.json" ] && [ -f "../../.perf-results/${preset}.json" ]; then | |
| echo "=== Comparing ${preset} ===" | |
| npx hytopia-bench compare \ | |
| "../../.perf-baseline/${preset}.json" \ | |
| "../../.perf-results/${preset}.json" \ | |
| --warn 5 --fail 10 || true | |
| fi | |
| done | |
| - name: Post results as PR comment | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const results = []; | |
| const dir = '.perf-results'; | |
| if (fs.existsSync(dir)) { | |
| for (const file of fs.readdirSync(dir)) { | |
| if (file.endsWith('.json')) { | |
| const data = JSON.parse(fs.readFileSync(`${dir}/${file}`, 'utf-8')); | |
| results.push(data); | |
| } | |
| } | |
| } | |
| if (results.length === 0) { | |
| console.log('No benchmark results to post'); | |
| return; | |
| } | |
| let body = '## Performance Benchmark Results\n\n'; | |
| for (const r of results) { | |
| body += `### ${r.scenario}\n`; | |
| body += `| Metric | Value |\n|--------|-------|\n`; | |
| if (r.baseline) { | |
| const b = r.baseline; | |
| body += `| Avg Tick | ${b.avgTickMs?.toFixed(2) ?? 'N/A'}ms |\n`; | |
| body += `| P95 Tick | ${b.p95TickMs?.toFixed(2) ?? 'N/A'}ms |\n`; | |
| body += `| P99 Tick | ${b.p99TickMs?.toFixed(2) ?? 'N/A'}ms |\n`; | |
| body += `| Max Tick | ${b.maxTickMs?.toFixed(2) ?? 'N/A'}ms |\n`; | |
| body += `| Over Budget | ${b.ticksOverBudgetPct?.toFixed(1) ?? 'N/A'}% |\n`; | |
| body += `| Avg Memory | ${b.avgMemoryMb?.toFixed(1) ?? 'N/A'}MB |\n`; | |
| } | |
| body += '\n'; | |
| } | |
| body += '\n---\n*Generated by @hytopia/perf-tools*\n'; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| - name: Upload results artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: perf-results | |
| path: .perf-results/ | |
| retention-days: 30 |