diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 0000000..01b6d25 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,31 @@ +name: Benchmarks + +on: + push: + branches: + - main + +jobs: + run_benchmarks: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + + - name: Run benchmarks + run: cargo bench + + - name: Upload benchmark results + uses: actions/upload-artifact@v4 + with: + name: benchmark-report + path: target/criterion/ # Capture all reports under target/criterion + if-no-files-found: warn # Optional: warn if no reports are found + retention-days: 7 # Optional: keep artifacts for 7 days diff --git a/Cargo.toml b/Cargo.toml index 6529b40..f8e33b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,5 @@ +cargo-features = ["edition2024"] + [package] name = "codeinput" version = "0.0.1-beta" @@ -25,13 +27,13 @@ syslog = ["slog-syslog"] human-panic = "2.0.0" better-panic = "0.3.0" log = "0.4.27" -clap_complete = "4.5.50" +clap_complete = "4.4.18" rand = "0.9.1" ignore = "0.4.23" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" bincode = {version= "2.0.1", features = ["serde"] } -git2 = { version = "0.20.2" } +git2 = { version = "0.18.3" } sha2 = { version = "0.10.9" } thiserror = "2.0.12" backtrace = "0.3.75" @@ -48,13 +50,14 @@ slog-stdlog = "4.1.1" slog-journald = {version = "2.2.0", optional = true } [dependencies.clap] -version = "4.5.38" +version = "4.4.18" features = ["cargo", "derive"] [dev-dependencies] assert_cmd = "2.0.17" predicates = "3.1.3" tempfile = "3.20" +criterion = { version = "0.5", features = ["html_reports"] } [profile.dev] opt-level = 0 @@ -72,6 +75,10 @@ lto = true debug-assertions = false codegen-units = 1 +[[bench]] +name = "parser_bench" +harness = false + [profile.test] opt-level = 1 debug = true diff --git a/benches/parser_bench.rs b/benches/parser_bench.rs new file mode 100644 index 0000000..729b66d --- /dev/null +++ b/benches/parser_bench.rs @@ -0,0 +1,46 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use codeinput::core::parser::parse_codeowners; +use std::io::Write; +use tempfile::NamedTempFile; + +fn benchmark_parser(c: &mut Criterion) { + let mut group = c.benchmark_group("parse_codeowners_group"); + + group.bench_function("parse_real_world_codeowners", |b| { + let mut temp_file = NamedTempFile::new().expect("Failed to create temp file"); + let codeowners_content = br#" +# This is a comment +*.ts @owner1 @owner2 +/docs/**/*.md @docs-owner @another-owner [docs] +# Another comment + +/apps/ @app-owner1 @app-owner2 [infra] [app] +/libs/ @lib-owner # Inline comment + /deep/nested/path/ @deep-owner1 @deep-owner2 @deep-owner3 [frontend] +# Empty lines follow + + +# Line with only spaces + +# Line with tabs +\t\t +# Complex patterns +[mM]akefile @user1 +src/**/*.java @java-dev @another-java-dev +*.{js,jsx,ts,tsx} @frontend-devs +/server/(app|test)/**/*.py @backend-devs [server] +docs/[^/]+/\.(md|txt)$ @doc-writers +"#; + temp_file.write_all(codeowners_content).expect("Failed to write to temp file"); + let file_path = temp_file.path(); + + b.iter(|| { + parse_codeowners(black_box(file_path.to_str().unwrap())) + }) + }); + + group.finish(); +} + +criterion_group!(benches, benchmark_parser); +criterion_main!(benches);