Skip to content

Commit 0e181c4

Browse files
committed
Integrate hyperlight-ci into CI workflows and Just recipes
- Add cargo alias (`cargo ci`) for convenient hyperlight-ci invocation - Update dep_benchmarks workflow to use `cargo ci bench` and generate a markdown report via `cargo ci bench-report`, posting results as a PR comment per hypervisor/cpu matrix entry - Add benchmarks job to ValidatePullRequest workflow with hypervisor and cpu matrix, gated behind docs-only and build-guests checks - Grant pull-requests: write permission for PR comment posting - Simplify Justfile bench recipes to delegate to `cargo ci bench` - Update benchmarking docs to reflect the new workflow Signed-off-by: Jorge Prendes <jorge.prendes@gmail.com>
1 parent d784a39 commit 0e181c4

6 files changed

Lines changed: 112 additions & 11 deletions

File tree

.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[alias] # command aliases
2+
ci = ["run", "--quiet", "--package=hyperlight-ci", "--"]

.github/workflows/ValidatePullRequest.yml

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ concurrency:
1515

1616
permissions:
1717
contents: write
18-
pull-requests: read
18+
pull-requests: write
1919

2020
jobs:
2121
docs-pr:
@@ -140,6 +140,86 @@ jobs:
140140
docs_only: ${{ needs.docs-pr.outputs.docs-only }}
141141
secrets: inherit
142142

143+
# Run benchmarks and post results as PR comment
144+
benchmarks:
145+
needs:
146+
- docs-pr
147+
- build-guests
148+
# Required because update-guest-locks is skipped on non-dependabot PRs,
149+
# and a skipped dependency transitively skips all downstream jobs.
150+
# See: https://github.com/actions/runner/issues/2205
151+
if: ${{ !cancelled() && !failure() }}
152+
strategy:
153+
fail-fast: false
154+
matrix:
155+
hypervisor: ['hyperv-ws2025', mshv3, kvm]
156+
cpu: [amd, intel]
157+
uses: ./.github/workflows/dep_benchmarks.yml
158+
secrets: inherit
159+
with:
160+
docs_only: ${{ needs.docs-pr.outputs.docs-only }}
161+
hypervisor: ${{ matrix.hypervisor }}
162+
cpu: ${{ matrix.cpu }}
163+
164+
# Collect all benchmark reports and post a single combined PR comment
165+
benchmark-comment:
166+
needs: benchmarks
167+
if: ${{ !cancelled() && !failure() }}
168+
runs-on: ubuntu-latest
169+
permissions:
170+
pull-requests: write
171+
steps:
172+
- name: Download all benchmark reports
173+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
174+
with:
175+
pattern: benchmark-report_*
176+
path: reports/
177+
178+
- name: Post combined benchmark results to PR
179+
uses: actions/github-script@v9
180+
with:
181+
script: |
182+
const fs = require('fs');
183+
const path = require('path');
184+
185+
const reportsDir = 'reports';
186+
if (!fs.existsSync(reportsDir)) {
187+
console.log('No benchmark reports found, skipping comment.');
188+
return;
189+
}
190+
191+
// Collect all report files from subdirectories
192+
const sections = [];
193+
const dirs = fs.readdirSync(reportsDir).sort();
194+
for (const dir of dirs) {
195+
const mdPath = path.join(reportsDir, dir, 'benchmark.md');
196+
if (!fs.existsSync(mdPath)) continue;
197+
198+
// Extract hypervisor/cpu from artifact name: benchmark-report_OS_hypervisor_cpu
199+
const parts = dir.replace('benchmark-report_', '').split('_');
200+
const os = parts[0];
201+
const hypervisor = parts.slice(1, -1).join('_');
202+
const cpu = parts[parts.length - 1];
203+
const label = `${hypervisor} / ${cpu} (${os})`;
204+
205+
const content = fs.readFileSync(mdPath, 'utf8').trim();
206+
sections.push(`<details>\n<summary><b>${label}</b></summary>\n\n${content}\n\n</details>`);
207+
}
208+
209+
if (sections.length === 0) {
210+
console.log('No benchmark report content found, skipping comment.');
211+
return;
212+
}
213+
214+
const body = `## Benchmark Results\n\n${sections.join('\n\n')}`;
215+
216+
await github.rest.issues.createComment({
217+
owner: context.repo.owner,
218+
repo: context.repo.repo,
219+
issue_number: context.issue.number,
220+
body: body,
221+
});
222+
143223
spelling:
144224
name: spell check with typos
145225
runs-on: ubuntu-latest
@@ -167,6 +247,8 @@ jobs:
167247
- build-test
168248
- run-examples
169249
- fuzzing
250+
- benchmarks
251+
- benchmark-comment
170252
- spelling
171253
- license-headers
172254
if: always()

.github/workflows/dep_benchmarks.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ on:
5656
required: false
5757
type: number
5858
default: 5
59-
6059
env:
6160
CARGO_TERM_COLOR: always
6261
RUST_BACKTRACE: full
@@ -133,7 +132,17 @@ jobs:
133132
continue-on-error: true
134133

135134
- name: Run benchmarks
136-
run: just bench-ci main
135+
run: just bench-ci
136+
137+
- name: Create benchmarks report
138+
run: cargo ci bench-report > target/criterion/benchmark.md
139+
140+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
141+
with:
142+
name: benchmark-report_${{ runner.os }}_${{ inputs.hypervisor }}_${{ inputs.cpu }}
143+
path: target/criterion/benchmark.md
144+
if-no-files-found: error
145+
retention-days: 1
137146

138147
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
139148
with:

Justfile

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ run-examples-like-ci config=default-target hypervisor="kvm":
177177

178178
benchmarks-like-ci config=default-target hypervisor="kvm":
179179
@# Run benchmarks
180-
{{ if config == "release" { "just bench-ci main" } else { "" } }}
180+
{{ if config == "release" { "just bench-ci" } else { "" } }}
181181

182182
fuzz-like-ci target config=default-target hypervisor="kvm":
183183
@# Run Fuzzing
@@ -400,13 +400,12 @@ bench-download os hypervisor cpu tag="":
400400
tar -zxvf target/benchmarks_{{ os }}_{{ hypervisor }}_{{ cpu }}.tar.gz -C target/criterion/ --strip-components=1
401401

402402
# Warning: compares to and then OVERWRITES the given baseline
403-
bench-ci baseline features="":
404-
@# Benchmarks are always run with release builds for meaningful results
405-
cargo bench --profile=release {{ if features =="" {''} else { "--features " + features } }} -- --verbose --save-baseline {{ baseline }}
403+
bench-ci features="":
404+
cargo ci bench --no-progress {{ if features == "" {''} else { "--features " + features } }}
406405

407406
bench features="":
408407
@# Benchmarks are always run with release builds for meaningful results
409-
cargo bench --profile=release {{ if features =="" {''} else { "--features " + features } }} -- --verbose
408+
cargo ci bench {{ if features == "" {''} else { "--features " + features } }}
410409

411410
###############
412411
### FUZZING ###

docs/benchmarking-hyperlight.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,6 @@ Found 1 outliers among 100 measurements (1.00%)
7272
7373
## Running benchmarks locally
7474
75-
Use `just bench` to run benchmarks with release builds (the only supported configuration). Comparing local benchmark results to github-saved benchmarks doesn't make much sense, since you'd be using different hardware, but you can use `just bench-download os hypervisor [tag] ` to download and extract the GitHub release benchmarks to the correct place folder. You can then run `just bench-ci main` to compare to (and overwrite) the previous release benchmarks. Note that `main` is the name of the baselines stored in GitHub.
75+
Use `just bench` to run benchmarks with release builds (the only supported configuration). Comparing local benchmark results to github-saved benchmarks doesn't make much sense, since you'd be using different hardware, but you can use `just bench-download os hypervisor [tag] ` to download and extract the GitHub release benchmarks to the correct place folder. You can then run `just bench-ci` to compare to (and overwrite) the previous release benchmarks. The name of the baselines stored in GitHub is `base`.
7676
7777
**Important**: The `just bench` command uses release builds by default to ensure meaningful performance measurements. For profiling purposes, you can compile benchmarks with debug symbols by running `cargo bench` directly.

src/hyperlight_ci/src/bench/discovery.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,18 @@ impl BenchmarkDiscovery {
6868
if let Some(filenames) = msg.get("filenames").and_then(|f| f.as_array()) {
6969
for f in filenames {
7070
if let Some(path) = f.as_str() {
71-
if !path.ends_with(".d") {
72-
binaries.push(PathBuf::from(path));
71+
// Skip non-executable artifacts:
72+
// .d = dep-info files (all platforms)
73+
// .pdb = debug symbols (Windows)
74+
// .dSYM = debug symbol bundles (macOS)
75+
// .dwp = DWARF packages (Linux, split-debuginfo)
76+
// .lib = import libraries (Windows)
77+
// .exp = export files (Windows)
78+
let dominated = [".d", ".pdb", ".dSYM", ".dwp", ".lib", ".exp"];
79+
if dominated.iter().any(|ext| path.ends_with(ext)) {
80+
continue;
7381
}
82+
binaries.push(PathBuf::from(path));
7483
}
7584
}
7685
}

0 commit comments

Comments
 (0)