Skip to content

Commit d475a23

Browse files
[ty] Use ThinVec for sparse kwargs bindings (#25457)
## Summary `multi_bindings_by_use` is only populated for kwargs expressions (like `f(**options)`), so it is empty or very small for most scopes. This PR keeps the mutable hash map while building the use-def map, then converts it into a sorted `ThinVec` for storage, which is much smaller (especially when empty). Lookups use binary search by `ScopedUseId`.
1 parent ec1d535 commit d475a23

3 files changed

Lines changed: 29 additions & 4 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ty_python_core/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ty_combine = { workspace = true }
2525

2626
bitflags = { workspace = true }
2727
bitvec = { workspace = true }
28-
get-size2 = { workspace = true, features = ["indexmap", "ordermap"] }
28+
get-size2 = { workspace = true, features = ["indexmap", "ordermap", "thin-vec"] }
2929
hashbrown = { workspace = true }
3030
itertools = { workspace = true }
3131
ruff_macros = { workspace = true, optional = true }
@@ -36,6 +36,7 @@ serde = { workspace = true, optional = true }
3636
serde_json = {workspace = true, optional = true }
3737
smallvec = { workspace = true }
3838
static_assertions = { workspace = true }
39+
thin-vec = { workspace = true }
3940
tracing = { workspace = true }
4041

4142
[dev-dependencies]

crates/ty_python_core/src/use_def.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@
244244
use ruff_index::{IndexVec, newtype_index};
245245
use ruff_text_size::TextRange;
246246
use rustc_hash::{FxBuildHasher, FxHashMap};
247+
use thin_vec::ThinVec;
247248

248249
use crate::ast_ids::ScopedUseId;
249250
use crate::definition::{Definition, DefinitionState};
@@ -327,7 +328,7 @@ pub struct UseDefMap<'db> {
327328
///
328329
/// This is only used for kwargs expressions, whose corresponding `bindings_by_use` entry
329330
/// is empty.
330-
multi_bindings_by_use: FxHashMap<ScopedUseId, Vec<Bindings>>,
331+
multi_bindings_by_use: MultiBindingsByUse,
331332

332333
/// Tracks the reachability constraint for statements and certain sub-expressions
333334
/// (e.g. ternary branches, boolean operator operands), keyed by their text range.
@@ -397,6 +398,27 @@ struct RangeInfo {
397398
in_type_checking_block: bool,
398399
}
399400

401+
#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
402+
struct MultiBindingsByUse(ThinVec<(ScopedUseId, Box<[Bindings]>)>);
403+
404+
impl MultiBindingsByUse {
405+
fn from_map(map: FxHashMap<ScopedUseId, Vec<Bindings>>) -> Self {
406+
let mut entries = map
407+
.into_iter()
408+
.map(|(use_id, bindings)| (use_id, bindings.into_boxed_slice()))
409+
.collect::<Vec<_>>();
410+
entries.sort_unstable_by_key(|(use_id, _)| use_id.as_u32());
411+
Self(entries.into_iter().collect())
412+
}
413+
414+
fn get(&self, use_id: ScopedUseId) -> Option<&[Bindings]> {
415+
self.0
416+
.binary_search_by_key(&use_id.as_u32(), |(candidate, _)| candidate.as_u32())
417+
.ok()
418+
.map(|index| self.0[index].1.as_ref())
419+
}
420+
}
421+
400422
pub enum ApplicableConstraints<'map, 'db> {
401423
UnboundBinding(NarrowingEvaluator<'map, 'db>),
402424
ConstrainedBindings(BindingWithConstraintsIterator<'map, 'db>),
@@ -444,7 +466,7 @@ impl<'db> UseDefMap<'db> {
444466
use_id: ScopedUseId,
445467
) -> impl Iterator<Item = BindingWithConstraintsIterator<'_, 'db>> {
446468
self.multi_bindings_by_use
447-
.get(&use_id)
469+
.get(use_id)
448470
.map(|member_bindings| {
449471
member_bindings.iter().map(|bindings| {
450472
self.bindings_iterator(bindings, BoundnessAnalysis::BasedOnUnboundVisibility)
@@ -1764,6 +1786,7 @@ impl<'db> UseDefMapBuilder<'db> {
17641786
}
17651787
}
17661788
self.reachability_constraints.mark_used(self.reachability);
1789+
let multi_bindings_by_use = MultiBindingsByUse::from_map(self.multi_bindings_by_use);
17671790

17681791
UseDefMap {
17691792
all_definitions: self.all_definitions,
@@ -1773,7 +1796,7 @@ impl<'db> UseDefMapBuilder<'db> {
17731796
interned_bindings,
17741797
interned_declarations,
17751798
bindings_by_use,
1776-
multi_bindings_by_use: self.multi_bindings_by_use,
1799+
multi_bindings_by_use,
17771800
range_reachability: self.range_reachability,
17781801
end_of_scope_symbols: self.symbol_states,
17791802
end_of_scope_members,

0 commit comments

Comments
 (0)