@@ -10,31 +10,33 @@ jobs:
1010 profile :
1111 runs-on : ubuntu-latest
1212 steps :
13- - uses : actions/checkout@v3
13+ - uses : actions/checkout@v4
1414 with :
1515 fetch-depth : 0
1616
1717 - name : Install Rust
18- uses : actions-rs/toolchain@v1
19- with :
20- profile : minimal
21- toolchain : stable
18+ uses : dtolnay/rust-toolchain@stable
2219
2320 - name : Install Valgrind
2421 run : |
2522 sudo apt update
2623 sudo apt install -y valgrind
2724
28- - name : Cache dependencies
29- uses : actions/cache@v3
25+ - name : Cache Rust dependencies
26+ uses : Swatinem/rust-cache@v2
27+ with :
28+ # Cache on Cargo.lock file
29+ cache-on-failure : true
30+
31+ - name : Cache iai-callgrind binary
32+ id : cache-iai
33+ uses : actions/cache@v4
3034 with :
31- path : |
32- ~/.cargo/registry
33- ~/.cargo/git
34- target
35- key : ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
35+ path : ~/.cargo/bin/iai-callgrind-runner
36+ key : ${{ runner.os }}-iai-callgrind-runner-0.12.3
3637
3738 - name : Install iai-callgrind
39+ if : steps.cache-iai.outputs.cache-hit != 'true'
3840 run : |
3941 cargo install iai-callgrind-runner@0.12.3
4042
4345 git fetch origin master:master
4446 git checkout master
4547
48+ - name : Get master commit SHA
49+ id : master-sha
50+ run : echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
51+
52+ - name : Cache benchmark baselines
53+ id : cache-benchmark-baselines
54+ uses : actions/cache@v4
55+ with :
56+ path : target/iai
57+ key : ${{ runner.os }}-benchmark-baselines-master-${{ steps.master-sha.outputs.sha }}
58+ restore-keys : |
59+ ${{ runner.os }}-benchmark-baselines-master-
60+
4661 - name : Run baseline benchmarks
62+ if : steps.cache-benchmark-baselines.outputs.cache-hit != 'true'
4763 run : |
64+ # Compile benchmarks
4865 cargo bench --bench compile_demo_art_iai -- --save-baseline=master
66+
67+ # Runtime benchmarks
68+ cargo bench --bench update_executor_iai -- --save-baseline=master
69+ cargo bench --bench run_once_iai -- --save-baseline=master
70+ cargo bench --bench run_cached_iai -- --save-baseline=master
4971
5072 - name : Checkout PR branch
5173 run : |
@@ -54,13 +76,33 @@ jobs:
5476 - name : Run PR benchmarks
5577 id : benchmark
5678 run : |
57- BENCH_OUTPUT=$(cargo bench --bench compile_demo_art_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
58- echo "BENCHMARK_OUTPUT<<EOF" >> $GITHUB_OUTPUT
59- echo "$BENCH_OUTPUT" >> $GITHUB_OUTPUT
79+ # Compile benchmarks
80+ COMPILE_OUTPUT=$(cargo bench --bench compile_demo_art_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
81+
82+ # Runtime benchmarks
83+ UPDATE_OUTPUT=$(cargo bench --bench update_executor_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
84+ RUN_ONCE_OUTPUT=$(cargo bench --bench run_once_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
85+ RUN_CACHED_OUTPUT=$(cargo bench --bench run_cached_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
86+
87+ # Store outputs
88+ echo "COMPILE_OUTPUT<<EOF" >> $GITHUB_OUTPUT
89+ echo "$COMPILE_OUTPUT" >> $GITHUB_OUTPUT
90+ echo "EOF" >> $GITHUB_OUTPUT
91+
92+ echo "UPDATE_OUTPUT<<EOF" >> $GITHUB_OUTPUT
93+ echo "$UPDATE_OUTPUT" >> $GITHUB_OUTPUT
94+ echo "EOF" >> $GITHUB_OUTPUT
95+
96+ echo "RUN_ONCE_OUTPUT<<EOF" >> $GITHUB_OUTPUT
97+ echo "$RUN_ONCE_OUTPUT" >> $GITHUB_OUTPUT
98+ echo "EOF" >> $GITHUB_OUTPUT
99+
100+ echo "RUN_CACHED_OUTPUT<<EOF" >> $GITHUB_OUTPUT
101+ echo "$RUN_CACHED_OUTPUT" >> $GITHUB_OUTPUT
60102 echo "EOF" >> $GITHUB_OUTPUT
61103
62104 - name : Make old comments collapsed by default
63- uses : actions/github-script@v6
105+ uses : actions/github-script@v7
64106 with :
65107 github-token : ${{secrets.GITHUB_TOKEN}}
66108 script : |
@@ -85,11 +127,15 @@ jobs:
85127 }
86128
87129 - name : Comment PR
88- uses : actions/github-script@v6
130+ uses : actions/github-script@v7
89131 with :
90132 github-token : ${{secrets.GITHUB_TOKEN}}
91133 script : |
92- const benchmarkOutput = JSON.parse(`${{ steps.benchmark.outputs.BENCHMARK_OUTPUT }}`);
134+ const compileOutput = JSON.parse(`${{ steps.benchmark.outputs.COMPILE_OUTPUT }}`);
135+ const updateOutput = JSON.parse(`${{ steps.benchmark.outputs.UPDATE_OUTPUT }}`);
136+ const runOnceOutput = JSON.parse(`${{ steps.benchmark.outputs.RUN_ONCE_OUTPUT }}`);
137+ const runCachedOutput = JSON.parse(`${{ steps.benchmark.outputs.RUN_CACHED_OUTPUT }}`);
138+
93139 let significantChanges = false;
94140 let commentBody = "";
95141
@@ -110,58 +156,97 @@ jobs:
110156 return str.padStart(len);
111157 }
112158
113- for (const benchmark of benchmarkOutput) {
114- if (benchmark.callgrind_summary && benchmark.callgrind_summary.summaries) {
115- const summary = benchmark.callgrind_summary.summaries[0];
116- const irDiff = summary.events.Ir;
117-
118- if (irDiff.diff_pct !== null) {
119- const changePercentage = formatPercentage(irDiff.diff_pct);
120- const color = irDiff.diff_pct > 0 ? "red" : "lime";
121-
122- commentBody += "---\n\n";
123- commentBody += `${benchmark.module_path} ${benchmark.id}:${benchmark.details}\n`;
124- commentBody += `Instructions: \`${formatNumber(irDiff.old)}\` (master) -> \`${formatNumber(irDiff.new)}\` (HEAD) : `;
125- commentBody += `$$\\color{${color}}${changePercentage.replace("%", "\\\\%")}$$\n\n`;
159+ function processBenchmarkOutput(benchmarkOutput, sectionTitle, isLast = false) {
160+ let sectionBody = "";
161+ let hasResults = false;
162+ let hasSignificantChanges = false;
163+
164+ for (const benchmark of benchmarkOutput) {
165+ if (benchmark.callgrind_summary && benchmark.callgrind_summary.summaries) {
166+ const summary = benchmark.callgrind_summary.summaries[0];
167+ const irDiff = summary.events.Ir;
126168
127- commentBody += "<details>\n<summary>Detailed metrics</summary>\n\n```\n";
128- commentBody += `Baselines: master| HEAD\n`;
129-
130- for (const [eventKind, costsDiff] of Object.entries(summary.events)) {
131- if (costsDiff.diff_pct !== null) {
132- const changePercentage = formatPercentage(costsDiff.diff_pct);
133- const line = `${padRight(eventKind, 20)} ${padLeft(formatNumber(costsDiff.old), 11)}|${padLeft(formatNumber(costsDiff.new), 11)} ${padLeft(changePercentage, 15)}`;
134- commentBody += `${line}\n`;
169+ if (irDiff.diff_pct !== null) {
170+ hasResults = true;
171+ const changePercentage = formatPercentage(irDiff.diff_pct);
172+ const color = irDiff.diff_pct > 0 ? "red" : "lime";
173+
174+ sectionBody += `**${benchmark.module_path} ${benchmark.id}:${benchmark.details}**\n`;
175+ sectionBody += `Instructions: \`${formatNumber(irDiff.old)}\` (master) → \`${formatNumber(irDiff.new)}\` (HEAD) : `;
176+ sectionBody += `$$\\color{${color}}${changePercentage.replace("%", "\\\\%")}$$\n\n`;
177+
178+ sectionBody += "<details>\n<summary>Detailed metrics</summary>\n\n```\n";
179+ sectionBody += `Baselines: master| HEAD\n`;
180+
181+ for (const [eventKind, costsDiff] of Object.entries(summary.events)) {
182+ if (costsDiff.diff_pct !== null) {
183+ const changePercentage = formatPercentage(costsDiff.diff_pct);
184+ const line = `${padRight(eventKind, 20)} ${padLeft(formatNumber(costsDiff.old), 11)}|${padLeft(formatNumber(costsDiff.new), 11)} ${padLeft(changePercentage, 15)}`;
185+ sectionBody += `${line}\n`;
186+ }
187+ }
188+
189+ sectionBody += "```\n</details>\n\n";
190+
191+ if (Math.abs(irDiff.diff_pct) > 5) {
192+ significantChanges = true;
193+ hasSignificantChanges = true;
135194 }
136- }
137-
138- commentBody += "```\n</details>\n\n";
139-
140- if (Math.abs(irDiff.diff_pct) > 5) {
141- significantChanges = true;
142195 }
143196 }
144197 }
198+
199+ if (hasResults) {
200+ // Wrap section in collapsible details, open only if there are significant changes
201+ const openAttribute = hasSignificantChanges ? " open" : "";
202+ const ruler = isLast ? "" : "\n\n---";
203+ return `<details${openAttribute}>\n<summary><h2>${sectionTitle}</h2></summary>\n\n${sectionBody}${ruler}\n</details>`;
204+ }
205+ return "";
145206 }
146207
147- const output = `
148- <details open>
149-
150- <summary>Performance Benchmark Results</summary>
151-
152- ${commentBody}
153-
154- </details>
155- `;
156-
157- if (significantChanges) {
158- github.rest.issues.createComment({
159- issue_number: context.issue.number,
160- owner: context.repo.owner,
161- repo: context.repo.repo,
162- body: output
163- });
208+ // Process each benchmark category
209+ const sections = [
210+ { output: compileOutput, title: "🔧 Graph Compilation" },
211+ { output: updateOutput, title: "🔄 Executor Update" },
212+ { output: runOnceOutput, title: "🚀 Render: Cold Execution" },
213+ { output: runCachedOutput, title: "⚡ Render: Cached Execution" }
214+ ];
215+
216+ // Generate sections and determine which ones have results
217+ const generatedSections = sections.map(({ output, title }) =>
218+ processBenchmarkOutput(output, title, true) // temporarily mark all as last
219+ ).filter(section => section.length > 0);
220+
221+ // Re-generate with correct isLast flags
222+ let sectionIndex = 0;
223+ const finalSections = sections.map(({ output, title }) => {
224+ const section = processBenchmarkOutput(output, title, true); // check if it has results
225+ if (section.length > 0) {
226+ const isLast = sectionIndex === generatedSections.length - 1;
227+ sectionIndex++;
228+ return processBenchmarkOutput(output, title, isLast);
229+ }
230+ return "";
231+ }).filter(section => section.length > 0);
232+
233+ // Combine all sections
234+ commentBody = finalSections.join("\n\n");
235+
236+ if (commentBody.length > 0) {
237+ const output = `<details open>\n<summary>Performance Benchmark Results</summary>\n\n${commentBody}\n</details>`;
238+
239+ if (significantChanges) {
240+ github.rest.issues.createComment({
241+ issue_number: context.issue.number,
242+ owner: context.repo.owner,
243+ repo: context.repo.repo,
244+ body: output
245+ });
246+ } else {
247+ console.log("No significant performance changes detected. Skipping comment.");
248+ console.log(output);
249+ }
164250 } else {
165- console.log("No significant performance changes detected. Skipping comment.");
166- console.log(output);
251+ console.log("No benchmark results to display.");
167252 }
0 commit comments