Skip to content

Commit ac348ee

Browse files
authored
Batch prefetch per fork (#10029)
Previously, the batch prefetcher was part of the solver loop, used across forks. This would lead to each preference in a fork being counted as a tried version, so that after 5 forks with the identical version, we would start batch prefetching. The reported numbers of tried versions are also reported. By tracking the batch prefetcher on the fork the numbers are corrected. An alternative would be tracking the actually tried versions, but that would mean more overhead in the top level solver loop when the current heuristic works. In `ecosystem/transformers`: ``` $ hyperfine --runs 10 --prepare "rm -f uv.lock" "../../target/release/uv lock --exclude-newer 2024-08-08T00:00:00Z" "uv lock --exclude-newer 2024-08-08T00:00:00Z" Benchmark 1: ../../target/release/uv lock --exclude-newer 2024-08-08T00:00:00Z Time (mean ± σ): 386.2 ms ± 6.1 ms [User: 396.0 ms, System: 144.5 ms] Range (min … max): 378.5 ms … 397.9 ms 10 runs Benchmark 2: uv lock --exclude-newer 2024-08-08T00:00:00Z Time (mean ± σ): 422.0 ms ± 5.5 ms [User: 459.6 ms, System: 190.3 ms] Range (min … max): 415.0 ms … 430.5 ms 10 runs Summary ../../target/release/uv lock --exclude-newer 2024-08-08T00:00:00Z ran 1.09 ± 0.02 times faster than uv lock --exclude-newer 2024-08-08T00:00:00Z ```
1 parent dd44245 commit ac348ee

File tree

2 files changed

+17
-5
lines changed

2 files changed

+17
-5
lines changed

crates/uv-resolver/src/resolver/batch_prefetch.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum BatchPrefetchStrategy {
3939
/// have to fetch the metadata for a lot of versions.
4040
///
4141
/// Note that these all heuristics that could totally prefetch lots of irrelevant versions.
42+
#[derive(Clone)]
4243
pub(crate) struct BatchPrefetcher {
4344
// Internal types.
4445
tried_versions: FxHashMap<PackageName, usize>,

crates/uv-resolver/src/resolver/mod.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,17 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
310310

311311
let root = PubGrubPackage::from(PubGrubPackageInner::Root(self.project.clone()));
312312
let pubgrub = State::init(root.clone(), MIN_VERSION.clone());
313-
let mut prefetcher = BatchPrefetcher::new(
313+
let prefetcher = BatchPrefetcher::new(
314314
self.capabilities.clone(),
315315
self.index.clone(),
316316
request_sink.clone(),
317317
);
318-
let state = ForkState::new(pubgrub, self.env.clone(), self.python_requirement.clone());
318+
let state = ForkState::new(
319+
pubgrub,
320+
self.env.clone(),
321+
self.python_requirement.clone(),
322+
prefetcher,
323+
);
319324
let mut preferences = self.preferences.clone();
320325
let mut forked_states = self.env.initial_forked_states(state);
321326
let mut resolutions = vec![];
@@ -393,7 +398,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
393398
else {
394399
// All packages have been assigned, the fork has been successfully resolved
395400
if tracing::enabled!(Level::DEBUG) {
396-
prefetcher.log_tried_versions();
401+
state.prefetcher.log_tried_versions();
397402
}
398403
debug!(
399404
"{} resolution took {:.3}s",
@@ -472,7 +477,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
472477
// (idempotent due to caching).
473478
self.request_package(next_package, url, index, &request_sink)?;
474479

475-
prefetcher.version_tried(next_package);
480+
state.prefetcher.version_tried(next_package);
476481

477482
let term_intersection = state
478483
.pubgrub
@@ -545,7 +550,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
545550

546551
// Only consider registry packages for prefetch.
547552
if url.is_none() {
548-
prefetcher.prefetch_batches(
553+
state.prefetcher.prefetch_batches(
549554
next_package,
550555
index,
551556
&version,
@@ -2259,13 +2264,18 @@ pub(crate) struct ForkState {
22592264
/// solution that omits Python 3.8 support.
22602265
python_requirement: PythonRequirement,
22612266
conflict_tracker: ConflictTracker,
2267+
/// Prefetch package versions for packages with many rejected versions.
2268+
///
2269+
/// Tracked on the fork state to avoid counting each identical version between forks as new try.
2270+
prefetcher: BatchPrefetcher,
22622271
}
22632272

22642273
impl ForkState {
22652274
fn new(
22662275
pubgrub: State<UvDependencyProvider>,
22672276
env: ResolverEnvironment,
22682277
python_requirement: PythonRequirement,
2278+
prefetcher: BatchPrefetcher,
22692279
) -> Self {
22702280
Self {
22712281
initial: None,
@@ -2279,6 +2289,7 @@ impl ForkState {
22792289
env,
22802290
python_requirement,
22812291
conflict_tracker: ConflictTracker::default(),
2292+
prefetcher,
22822293
}
22832294
}
22842295

0 commit comments

Comments
 (0)