From ae52f7b5040a1e5816bc9bddcf374202470c48b2 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Thu, 27 Aug 2026 09:31:23 +0200 Subject: [PATCH] perf: bypass try_join_all for single requirements Keep union candidate requests concurrent while avoiding try_join_all's per-future bookkeeping for the common single-version-set case. Also lower solve-snapshot's default per-solve timeout to 10 seconds. --- src/solver/encoding.rs | 47 ++++++++++++++++++++------------ tools/solve-snapshot/src/main.rs | 2 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/solver/encoding.rs b/src/solver/encoding.rs index 41563eaa..41b60bb4 100644 --- a/src/solver/encoding.rs +++ b/src/solver/encoding.rs @@ -21,6 +21,33 @@ type PendingTask<'cache, D> = LocalBoxFuture<'cache, Result = Option<(ConditionId, Vec>>)>; +/// Fetches each version set's sorted candidates while avoiding `try_join_all`'s +/// per-future bookkeeping for the overwhelmingly common single-version-set case. +/// Union members remain concurrent for dependency providers whose futures yield. +async fn get_requirement_candidates( + cache: &SolverCache, + requirement: Requirement, +) -> Result, Box> { + match requirement { + Requirement::Single(version_set) => Ok(vec![ + cache + .get_or_cache_sorted_candidates_for_version_set(version_set) + .await?, + ]), + Requirement::Union(version_set_union) => { + futures::future::try_join_all( + cache + .provider() + .version_sets_in_union(version_set_union) + .map(|version_set| { + cache.get_or_cache_sorted_candidates_for_version_set(version_set) + }), + ) + .await + } + } +} + /// An object that is responsible for encoding information from the dependency /// provider into rules and variables that are used by the solver. /// @@ -853,15 +880,7 @@ impl<'a, 'cache, D: DependencyProvider> Encoder<'a, 'cache, D> { ) { let cache = self.cache; self.queue_future(async move { - let candidates = futures::future::try_join_all( - requirement - .requirement - .version_sets(cache.provider()) - .map(|version_set| { - cache.get_or_cache_sorted_candidates_for_version_set(version_set) - }), - ) - .await?; + let candidates = get_requirement_candidates(cache, requirement.requirement).await?; Ok(TaskResult::RequirementCandidates( RequirementCandidatesAvailable { @@ -1039,15 +1058,7 @@ impl<'a, 'cache, D: DependencyProvider> Encoder<'a, 'cache, D> { })) .await?; - let candidates = futures::future::try_join_all( - requirement - .requirement - .version_sets(cache.provider()) - .map(|version_set| { - cache.get_or_cache_sorted_candidates_for_version_set(version_set) - }), - ) - .await?; + let candidates = get_requirement_candidates(cache, requirement.requirement).await?; Ok(TaskResult::RequirementCandidates( RequirementCandidatesAvailable { diff --git a/tools/solve-snapshot/src/main.rs b/tools/solve-snapshot/src/main.rs index fcaf047b..d7a523b9 100644 --- a/tools/solve-snapshot/src/main.rs +++ b/tools/solve-snapshot/src/main.rs @@ -30,7 +30,7 @@ struct Opts { /// The timeout to use for solving requirements in seconds. If a solve takes /// longer if will be cancelled. - #[clap(long, default_value = "60")] + #[clap(long, default_value = "10")] timeout: u64, /// The random seed to use for generating the requirements.