1+ name : Performance Benchmarks
2+
3+ permissions :
4+ contents : read
5+ pull-requests : write
6+
7+ on :
8+ pull_request :
9+ types : [closed]
10+ branches : [main]
11+ # Allow manual triggering for testing
12+ workflow_dispatch :
13+
14+ jobs :
15+ benchmark :
16+ # Run on merged PRs or manual trigger
17+ if : github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
18+ runs-on : ubuntu-latest
19+
20+ steps :
21+ - uses : actions/checkout@v4
22+
23+ - name : Install uv
24+ uses : astral-sh/setup-uv@v4
25+ with :
26+ enable-cache : true
27+ cache-dependency-glob : " **/pyproject.toml"
28+
29+ - name : Set up Python 3.12
30+ run : uv python install 3.12
31+
32+ - name : Install all dependencies
33+ run : uv sync --group all_loaders --group test --group dev
34+
35+ - name : Run performance benchmarks
36+ env :
37+ USE_TESTCONTAINERS : " true"
38+ TESTCONTAINERS_RYUK_DISABLED : " true"
39+ PERF_ENV : " github-actions"
40+ run : |
41+ uv run pytest tests/performance/ -v -m "performance" \
42+ --tb=short \
43+ -k "not snowflake" \
44+ 2>&1 | tee benchmark_output.txt
45+
46+ # Copy benchmark results for the comment
47+ if [ -f performance_benchmarks.json ]; then
48+ cp performance_benchmarks.json benchmark_results.json
49+ else
50+ echo '{}' > benchmark_results.json
51+ fi
52+
53+ - name : Write results to job summary
54+ run : |
55+ echo "## Performance Benchmark Results" >> $GITHUB_STEP_SUMMARY
56+ echo "" >> $GITHUB_STEP_SUMMARY
57+ echo "**Git Commit:** \`${GITHUB_SHA::8}\`" >> $GITHUB_STEP_SUMMARY
58+ echo "**Environment:** GitHub Actions" >> $GITHUB_STEP_SUMMARY
59+ echo "" >> $GITHUB_STEP_SUMMARY
60+ echo "### Raw Results" >> $GITHUB_STEP_SUMMARY
61+ echo "" >> $GITHUB_STEP_SUMMARY
62+ echo "\`\`\`json" >> $GITHUB_STEP_SUMMARY
63+ cat benchmark_results.json >> $GITHUB_STEP_SUMMARY
64+ echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
65+
66+ - name : Post benchmark results to PR
67+ if : github.event_name == 'pull_request'
68+ uses : actions/github-script@v7
69+ with :
70+ script : |
71+ const fs = require('fs');
72+
73+ // Read benchmark results
74+ let benchmarkData = {};
75+ try {
76+ benchmarkData = JSON.parse(fs.readFileSync('benchmark_results.json', 'utf8'));
77+ } catch (e) {
78+ console.log('No benchmark results file found');
79+ }
80+
81+ // Read test output for summary
82+ let testOutput = '';
83+ try {
84+ testOutput = fs.readFileSync('benchmark_output.txt', 'utf8');
85+ } catch (e) {
86+ console.log('No test output file found');
87+ }
88+
89+ // Extract summary from pytest output
90+ const summaryMatch = testOutput.match(/=+ ([\d\w\s,]+) in [\d.]+s =+/);
91+ const summary = summaryMatch ? summaryMatch[1] : 'Unknown';
92+
93+ // Format benchmark results as a table
94+ let resultsTable = '';
95+ const entries = Object.entries(benchmarkData);
96+
97+ if (entries.length > 0) {
98+ resultsTable = '| Loader | Test | Throughput (rows/sec) | Memory (MB) | Duration (s) | Dataset Size |\n';
99+ resultsTable += '|--------|------|----------------------|-------------|--------------|-------------|\n';
100+
101+ for (const [key, data] of entries) {
102+ resultsTable += `| ${data.loader_type} | ${data.test_name} | ${data.throughput_rows_per_sec?.toFixed(0) || 'N/A'} | ${data.memory_mb?.toFixed(2) || 'N/A'} | ${data.duration_seconds?.toFixed(2) || 'N/A'} | ${data.dataset_size?.toLocaleString() || 'N/A'} |\n`;
103+ }
104+ } else {
105+ resultsTable = '_No benchmark data recorded_';
106+ }
107+
108+ // Create comment body using array join to avoid YAML parsing issues
109+ const body = [
110+ '## Performance Benchmark Results',
111+ '',
112+ `**Test Summary:** ${summary}`,
113+ `**Git Commit:** \`${context.sha.substring(0, 8)}\``,
114+ '**Environment:** GitHub Actions',
115+ '',
116+ '### Results',
117+ '',
118+ resultsTable,
119+ '',
120+ '<details>',
121+ '<summary>Raw JSON Results</summary>',
122+ '',
123+ '```json',
124+ JSON.stringify(benchmarkData, null, 2),
125+ '```',
126+ '',
127+ '</details>'
128+ ].join('\n');
129+
130+ // Post comment to PR
131+ await github.rest.issues.createComment({
132+ owner: context.repo.owner,
133+ repo: context.repo.repo,
134+ issue_number: context.payload.pull_request.number,
135+ body: body
136+ });
0 commit comments