diff --git a/Cargo.toml b/Cargo.toml index 5b14909c..49362ab5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,8 @@ include = ["Cargo.toml", "LICENSE", "README.md", "src/**", "tests/**", "examples [dependencies] indexmap = "2.6.0" -log = "0.4.22" # for debug logs in tests +# for debug logs in tests +log = "0.4.22" priority-queue = "2.1.1" rustc-hash = ">=1.0.0, <3.0.0" serde = { version = "1.0", features = ["derive"], optional = true } @@ -42,6 +43,10 @@ version-ranges = { version = "0.1.0", path = "version-ranges", features = ["prop [features] serde = ["dep:serde", "version-ranges/serde"] +[[bench]] +name = "backtracking" +harness = false + [[bench]] name = "large_case" harness = false diff --git a/benches/backtracking.rs b/benches/backtracking.rs new file mode 100644 index 00000000..689e437d --- /dev/null +++ b/benches/backtracking.rs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! This bench monitors the performance of backtracking and term intersection. +//! +//! Dependencies are constructed in a way that all versions need to be tested before finding a solution. + +use criterion::*; +use pubgrub::OfflineDependencyProvider; +use version_ranges::Ranges; + +/// This benchmark is a simplified reproduction of one of the patterns found in the `solana-*` crates from Cargo: +/// * `solana-archiver-lib v1.1.12` depends on many layers of other solana crates with req `>= 1.1.12`. +/// * each version `1.x.y` higher than `1.5.15` of a solana crate depends on other solana crates with req `= 1.x.y`. +/// * `solana-crate-features` depends on `cc` with the `num_cpus` feature, which doesn't exist in recent versions of `cc`. +fn backtracking_singletons(c: &mut Criterion, package_count: u32, version_count: u32) { + let mut dependency_provider = OfflineDependencyProvider::>::new(); + + dependency_provider.add_dependencies(0u32, 0u32, [(1u32, Ranges::full())]); + dependency_provider.add_dependencies(1u32, 0u32, []); + + for n in 1..package_count { + for v in 1..version_count { + dependency_provider.add_dependencies(n, v, [(n + 1, Ranges::singleton(v))]); + } + } + + c.bench_function("backtracking_singletons", |b| { + b.iter(|| { + let _ = pubgrub::resolve(&dependency_provider, 0u32, 0u32); + }) + }); +} + +/// This benchmark is a simplified reproduction of one of the patterns found in the `solana-*` crates from Cargo: +/// * `solana-archiver-lib v1.1.12` depends on many layers of other solana crates with req `>= 1.1.12`. +/// * `solana-archiver-lib v1.1.12` also depends on `ed25519-dalek v1.0.0-pre.3`. +/// * each version `1.x.y` higher than `1.5.15` of a solana crate depends on other solana crates with req `= 1.x.y`. +/// * `solana-crate-features >= 1.2.17` depends on `ed25519-dalek v1.0.0-pre.4` or a higher incompatible version. +fn backtracking_disjoint_versions(c: &mut Criterion, package_count: u32, version_count: u32) { + let mut dependency_provider = OfflineDependencyProvider::>::new(); + + let root_deps = [(1u32, Ranges::full()), (u32::MAX, Ranges::singleton(0u32))]; + dependency_provider.add_dependencies(0u32, 0u32, root_deps); + + dependency_provider.add_dependencies(1u32, 0u32, []); + + for n in 1..package_count { + for v in 1..version_count { + dependency_provider.add_dependencies(n, v, [(n + 1, Ranges::singleton(v))]); + } + } + for v in 1..version_count { + dependency_provider.add_dependencies(package_count, v, [(u32::MAX, Ranges::singleton(v))]); + } + + for v in 0..version_count { + dependency_provider.add_dependencies(u32::MAX, v, []); + } + + c.bench_function("backtracking_disjoint_versions", |b| { + b.iter(|| { + let _ = pubgrub::resolve(&dependency_provider, 0u32, 0u32); + }) + }); +} + +/// This benchmark is a simplified reproduction of one of the patterns found in the `solana-*` crates from Cargo: +/// * `solana-archiver-lib v1.1.12` depends on many layers of other solana crates with req `>= 1.1.12`. +/// * each version `1.x.y` lower than `1.5.14` of a solana crate depends on other solana crates with req `>= 1.x.y`. +/// * `solana-crate-features` depends on `cc` with the `num_cpus` feature, which doesn't exist in recent versions of `cc`. +fn backtracking_ranges(c: &mut Criterion, package_count: u32, version_count: u32) { + let mut dependency_provider = OfflineDependencyProvider::>::new(); + + dependency_provider.add_dependencies(0u32, 0u32, [(1u32, Ranges::full())]); + dependency_provider.add_dependencies(1u32, 0u32, []); + + for n in 1..package_count { + for v in 1..version_count { + let r = Ranges::higher_than(version_count - v); + dependency_provider.add_dependencies(n, v, [(n + 1, r)]); + } + } + + c.bench_function("backtracking_ranges", |b| { + b.iter(|| { + let _ = pubgrub::resolve(&dependency_provider, 0u32, 0u32); + }) + }); +} + +fn bench_group(c: &mut Criterion) { + backtracking_singletons(c, 100, 500); + backtracking_disjoint_versions(c, 300, 200); + backtracking_ranges(c, 5, 200); +} + +criterion_group!(benches, bench_group); +criterion_main!(benches); diff --git a/benches/large_case.rs b/benches/large_case.rs index b05a1017..899d8a0d 100644 --- a/benches/large_case.rs +++ b/benches/large_case.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 + use std::time::Duration; use criterion::*; diff --git a/benches/sudoku.rs b/benches/sudoku.rs index 65d71e6a..37dac51f 100644 --- a/benches/sudoku.rs +++ b/benches/sudoku.rs @@ -56,8 +56,8 @@ fn from_board(b: &str) -> Vec<(SudokuPackage, Range>)> { if let Some(val) = val.chars().next().unwrap().to_digit(10) { out.push(( SudokuPackage::Cell { - row: (row + 1).try_into().unwrap(), - col: (col + 1).try_into().unwrap(), + row: row + 1, + col: col + 1, }, Range::singleton(val as usize), )); diff --git a/test-examples/large_case_u16_NumberVersion.ron b/test-examples/large_case_u16_NumberVersion.ron index de52b769..56801253 100644 --- a/test-examples/large_case_u16_NumberVersion.ron +++ b/test-examples/large_case_u16_NumberVersion.ron @@ -5521,4 +5521,4 @@ 18: {}, 19: {}, }, -} \ No newline at end of file +}