-
Notifications
You must be signed in to change notification settings - Fork 6
ci: introduce benchmarks for CI #1016
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 49 commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
1570270
introduce ci job & also a converion script
techraed 38ed517
add tested workflow
techraed 0b520c4
remove file
techraed 9edf8ab
try trigger CI
techraed ed4eaad
adjust CI job
techraed 1bd9b60
add summary CI
techraed 8442c68
try include issues
techraed 6b07ebc
test comments
techraed a51e3a2
turn back to usual job config
techraed 97510d7
try again alert threshold 0%
techraed 3191f42
define comment-on-alert
techraed 18141b2
add possible fix
techraed 392c375
add gh token
techraed 1309e61
try bench
techraed cb82b74
cat bench files
techraed 974860b
Trigger CI
techraed 28a0bed
remove filters
techraed c7bfcc8
include wasm-opt
techraed a457020
add clean-up
techraed 0f91cb8
adjust solution, remove conversion script, check weights
techraed 21e75d8
bump wasm-opt version
techraed faa90bc
introduce first iter solution
techraed 582c868
introduce pre-check to find if benches were forgotten to be updated
techraed 137b2dc
test git show
techraed e972449
comment make bench
techraed cca912c
add fetch-depth
techraed 06c9345
test diff test on a real diff
techraed 066ad11
uncomment full job
techraed dce980d
test case when changes weren't benched
techraed 348181a
test case when benched with significant changes
techraed 951265f
refactor benches analyzing scripts
techraed 40060ee
clean-up refactoring for bench-analyzer
techraed f6e434c
clean-up the job
techraed 793f53d
fix bench-analyzer build, add label check to the job
techraed f983c35
fmt
techraed 0cc787e
fix job
techraed 2e8a7e8
check with no paths
techraed 42a3996
add paths
techraed 6b30e3a
trigger benches on changing the benchmarks crate
techraed 1a86470
fix event type
techraed 8d26933
empty commit must not trigger CI, because didn't change paths
techraed 388b2da
test not triggered
techraed 594e3ea
fix job
techraed 229cd08
no label commit
techraed 1521fde
no label commit2
techraed 4d06ef5
add not benched changes
techraed 369788e
re-bench
techraed 994dfe5
always post benches, adjust counter-bench and re-bench
techraed a6de3c7
trigger CI
techraed 19e7d82
remove redundant cargo term color setting
techraed File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| name: '[rs] Benchmarks' | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, labeled] | ||
| paths: | ||
| - 'benchmarks/**' | ||
| - 'rs/**' | ||
| - 'Cargo.lock' | ||
| - 'Cargo.toml' | ||
| - '.github/workflows/rs-bench.yml' | ||
| - 'examples/**' | ||
| push: | ||
| branches: [master] | ||
| paths: | ||
| - 'benchmarks/**' | ||
| - 'rs/**' | ||
| - 'Cargo.lock' | ||
| - 'Cargo.toml' | ||
| - '.github/workflows/rs-bench.yml' | ||
| - 'examples/**' | ||
|
|
||
| env: | ||
| CARGO_TERM_COLOR: always | ||
| RUST_BACKTRACE: 1 | ||
|
|
||
| jobs: | ||
| benchmark: | ||
| if: (github.event_name == 'push') || | ||
| (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-benchmarks')) | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| issues: write | ||
| steps: | ||
| - name: Checkout PR branch | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Free Disk Space | ||
| uses: ./.github/actions/free-disk-space | ||
|
|
||
| - name: Install wasm-opt | ||
| uses: ./.github/actions/install-wasm-utils | ||
|
|
||
| - name: Build bench-analyzer | ||
| run: | | ||
| make build-bench-analyzer | ||
|
|
||
| - name: Copy current benchmarks for a diff test | ||
| run: | | ||
| cp benchmarks/bench_data.json benchmarks/bench_data_before_bench_run.json | ||
|
|
||
| - name: Run benchmarks | ||
| run: | | ||
| make bench | ||
|
|
||
| # The check is done with a threshold test | ||
| - name: Check current branch has actual benchmark data | ||
| if: github.event_name == 'pull_request' | ||
| run: | | ||
| ./target/debug/bench-analyzer --current=benchmarks/bench_data.json --other=benchmarks/bench_data_before_bench_run.json --threshold=1 | ||
|
|
||
| # If diff check passes, compare with master baseline | ||
| # First copy baseline JSON from master branch | ||
| - name: Copy baseline benchmarks from master branch | ||
| if: github.event_name == 'pull_request' | ||
| run: | | ||
| git fetch origin master:refs/remotes/origin/master | ||
| git show origin/master:benchmarks/bench_data.json > benchmarks/baseline.json | ||
|
|
||
| # Now compare benchmarks and generate markdown table | ||
| - name: Compare Benchmarks vs Master | ||
| if: github.event_name == 'pull_request' | ||
| run: | | ||
| ./target/debug/bench-analyzer --current=benchmarks/bench_data.json --other=benchmarks/baseline.json --output=benchmarks/comparison.md | ||
| env: | ||
| CARGO_TERM_COLOR: always | ||
|
|
||
| # Read the comparison markdown for the comment | ||
| - name: Read Comparison Result | ||
| if: github.event_name == 'pull_request' | ||
| id: comparison | ||
| run: | | ||
| echo 'COMPARISON_TABLE<<EOF' >> $GITHUB_OUTPUT | ||
| cat benchmarks/comparison.md >> $GITHUB_OUTPUT | ||
| echo 'EOF' >> $GITHUB_OUTPUT | ||
|
|
||
| # Comment the comparison table on the PR | ||
| - name: Comment PR with Benchmark Comparison | ||
| if: github.event_name == 'pull_request' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const comparisonTable = `${{ steps.comparison.outputs.COMPARISON_TABLE }}`; | ||
|
|
||
| const commentBody = `${comparisonTable} | ||
|
|
||
| --- | ||
| <sub>🤖 This comment was automatically generated by the benchmark comparison workflow at ${{ github.sha }}.</sub>`; | ||
|
|
||
| // Always create a new comment | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: commentBody | ||
| }); | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| use anyhow::{Context, Result, anyhow}; | ||
| use benchmarks::{ | ||
| BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataFile, | ||
| }; | ||
| use clap::Parser; | ||
| use std::{fs, path::PathBuf}; | ||
|
|
||
| #[derive(Parser)] | ||
| #[command(version, about, long_about = None)] | ||
| #[command(name = "bench-analyzer")] | ||
| #[command( | ||
| about = "A tool for analyzing benchmark data by comparing current and previous benchmark results." | ||
| )] | ||
| struct Cli { | ||
| /// Current benchmark data file | ||
| #[arg(long)] | ||
| current: PathBuf, | ||
|
|
||
| /// Other benchmark data file | ||
| #[arg(long)] | ||
| other: PathBuf, | ||
|
|
||
| /// Threshold percentage for failure | ||
| #[arg(long)] | ||
| threshold: Option<f64>, | ||
|
|
||
| /// Report markdown file | ||
| #[arg(long)] | ||
| output: Option<PathBuf>, | ||
| } | ||
|
|
||
| fn main() -> Result<()> { | ||
| let cli = Cli::parse(); | ||
|
|
||
| analyze_benches(cli.current, cli.other, cli.output, cli.threshold) | ||
| } | ||
|
|
||
| fn analyze_benches( | ||
| current: PathBuf, | ||
| other: PathBuf, | ||
| report_output: Option<PathBuf>, | ||
| threshold: Option<f64>, | ||
| ) -> Result<()> { | ||
| // Get benches data from the provided files. | ||
| let (current_data, other_data) = get_bench_data(current, other)?; | ||
|
|
||
| // Flag to track if any benchmarks fail the threshold check. | ||
| let mut threshold_failed = threshold.map(|_| false); | ||
|
|
||
| // Create unfinished report. | ||
| let mut report = current_data | ||
| .into_iter() | ||
| .zip(other_data) | ||
| .map( | ||
| // Create a comparison entity for each benchmark category. | ||
| |((current_category, current_value), (other_category, other_value))| { | ||
| assert_eq!(current_category, other_category, "Categories do not match"); | ||
|
|
||
| let comparison = BenchCategoryComparison::new( | ||
| current_category, | ||
| current_value, | ||
| other_value, | ||
| threshold, | ||
| ); | ||
|
|
||
| if matches!(threshold_failed, Some(false)) && comparison.has_failed_threshold() { | ||
| let _ = threshold_failed.insert(true); | ||
| } | ||
|
|
||
| comparison | ||
| }, | ||
| ) | ||
| .fold(initialize_report(), |mut report, comparison| { | ||
| // Add each comparison to the report. | ||
| add_comparison_to_report(&mut report, comparison); | ||
| report | ||
| }); | ||
|
|
||
| // Finish the report. | ||
| add_report_conclusion(&mut report, threshold, threshold_failed); | ||
|
|
||
| // Printing the finalized report. | ||
| println!("{report}"); | ||
|
|
||
| // If any benchmarks failed the threshold check, return an error. | ||
| if matches!(threshold_failed, Some(true)) { | ||
| return Err(anyhow!("Benchmark contains tests failing the threshold.")); | ||
| } | ||
|
|
||
| // If an output path is provided, write the report to that file. | ||
| if let Some(report_output) = report_output { | ||
| fs::write(&report_output, &report).context("Failed to write report output")?; | ||
|
|
||
| println!( | ||
| "\nComparison table written to '{}'", | ||
| report_output.display() | ||
| ); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn get_bench_data(current: PathBuf, previous: PathBuf) -> Result<(BenchData, BenchData)> { | ||
| let mut current_file = | ||
| BenchDataFile::open(current).context("Failed to open current benchmark data file")?; | ||
| let mut previous_file = | ||
| BenchDataFile::open(previous).context("Failed to open previous benchmark data file")?; | ||
|
|
||
| let current_data = current_file.read_bench_data()?; | ||
| let previous_data = previous_file.read_bench_data()?; | ||
|
|
||
| Ok((current_data, previous_data)) | ||
| } | ||
|
|
||
| fn initialize_report() -> String { | ||
| let mut report = String::new(); | ||
| report.push_str("## 🔬 Benchmark Comparison\n\n"); | ||
| report.push_str("| Benchmark | Current | Baseline | Change | Change % | Status |\n"); | ||
| report.push_str("|-----------|---------|----------|---------|----------|--------|\n"); | ||
|
|
||
| report | ||
| } | ||
|
|
||
| fn add_comparison_to_report(report: &mut String, comparison: BenchCategoryComparison) { | ||
| let BenchCategoryComparisonReport { | ||
| category, | ||
| current, | ||
| other, | ||
| diff_sign, | ||
| diff, | ||
| diff_percent_sign, | ||
| diff_percent, | ||
| status, | ||
| } = comparison.into(); | ||
| report.push_str(&format!( | ||
| "| {category} | {current} | {other} | {diff_sign}{diff} | {diff_percent_sign}{diff_percent:.2}% | {status} |\n", | ||
| )); | ||
| } | ||
|
|
||
| fn add_report_conclusion( | ||
| report: &mut String, | ||
| threshold: Option<f64>, | ||
| threshold_failed: Option<bool>, | ||
| ) { | ||
| match threshold_failed { | ||
| Some(true) => { | ||
| let threshold = threshold.expect("threshold is required when threshold_failed is true"); | ||
| let err_str = format!("\n❌ Benchmark threshold {threshold:.1}% check failed!\n"); | ||
| report.push_str(&err_str); | ||
| } | ||
| Some(false) => { | ||
| report.push_str("\n✅ All benchmark differences are within acceptable thresholds."); | ||
| } | ||
| None => { | ||
| report.push_str("\n### Legend\n- 🚀 Significant improvement (>5% reduction)\n- 👍 Minor improvement (<5% reduction)\n- ✅ No significant change\n- ⚠️ Minor regression (<5% increase)\n- ❌ Significant regression (>5% increase)\n"); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.