Skip to content

Commit 47a0cd2

Browse files
authored
[Turbopack] Inline commonly used storages (#75284)
### What? * move code from macro into generic struct * move dynamic storage access into separate struct * generate inline storage for common data
1 parent 111f334 commit 47a0cd2

File tree

11 files changed

+887
-489
lines changed

11 files changed

+887
-489
lines changed

Cargo.lock

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

turbopack/crates/turbo-tasks-auto-hash-map/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ edition = "2021"
99
workspace = true
1010

1111
[dependencies]
12+
hashbrown = { workspace = true, features = ["serde"]}
1213
rustc-hash = { workspace = true }
1314
serde = { workspace = true, features = ["derive"] }
1415
smallvec = { workspace = true }

turbopack/crates/turbo-tasks-auto-hash-map/src/map.rs

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::{
22
borrow::Borrow,
3-
collections::HashMap,
43
fmt::{Debug, Formatter},
54
hash::{BuildHasher, BuildHasherDefault, Hash},
65
marker::PhantomData,
76
};
87

8+
use hashbrown::hash_map::HashMap;
99
use rustc_hash::FxHasher;
1010
use serde::{
1111
de::{MapAccess, Visitor},
@@ -176,12 +176,10 @@ impl<K: Eq + Hash, V, H: BuildHasher + Default, const I: usize> AutoMap<K, V, H,
176176
None => Entry::Vacant(VacantEntry::List { this, list, key }),
177177
},
178178
AutoMap::Map(map) => match map.entry(key) {
179-
std::collections::hash_map::Entry::Occupied(entry) => {
179+
hashbrown::hash_map::Entry::Occupied(entry) => {
180180
Entry::Occupied(OccupiedEntry::Map { this, entry })
181181
}
182-
std::collections::hash_map::Entry::Vacant(entry) => {
183-
Entry::Vacant(VacantEntry::Map(entry))
184-
}
182+
hashbrown::hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry::Map(entry)),
185183
},
186184
}
187185
}
@@ -198,10 +196,10 @@ impl<K: Eq + Hash, V, H: BuildHasher + Default, const I: usize> AutoMap<K, V, H,
198196
None => RawEntry::Vacant(VacantRawEntry::List { this, list }),
199197
},
200198
AutoMap::Map(map) => match map.raw_entry_mut().from_key(key) {
201-
std::collections::hash_map::RawEntryMut::Occupied(entry) => {
199+
hashbrown::hash_map::RawEntryMut::Occupied(entry) => {
202200
RawEntry::Occupied(OccupiedRawEntry::Map { this, entry })
203201
}
204-
std::collections::hash_map::RawEntryMut::Vacant(entry) => {
202+
hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
205203
RawEntry::Vacant(VacantRawEntry::Map(entry))
206204
}
207205
},
@@ -397,7 +395,7 @@ impl<'a, K, V, H, const I: usize> IntoIterator for &'a AutoMap<K, V, H, I> {
397395

398396
pub enum Iter<'a, K, V> {
399397
List(std::slice::Iter<'a, (K, V)>),
400-
Map(std::collections::hash_map::Iter<'a, K, V>),
398+
Map(hashbrown::hash_map::Iter<'a, K, V>),
401399
}
402400

403401
impl<'a, K, V> Iterator for Iter<'a, K, V> {
@@ -431,7 +429,7 @@ impl<K, V> Clone for Iter<'_, K, V> {
431429

432430
pub enum IterMut<'a, K, V> {
433431
List(std::slice::IterMut<'a, (K, V)>),
434-
Map(std::collections::hash_map::IterMut<'a, K, V>),
432+
Map(hashbrown::hash_map::IterMut<'a, K, V>),
435433
}
436434

437435
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
@@ -454,7 +452,7 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> {
454452

455453
pub enum IntoIter<K, V, const I: usize> {
456454
List(smallvec::IntoIter<[(K, V); I]>),
457-
Map(std::collections::hash_map::IntoIter<K, V>),
455+
Map(hashbrown::hash_map::IntoIter<K, V>),
458456
}
459457

460458
impl<K, V, const I: usize> Iterator for IntoIter<K, V, I> {
@@ -477,7 +475,7 @@ impl<K, V, const I: usize> Iterator for IntoIter<K, V, I> {
477475

478476
pub enum Values<'a, K, V> {
479477
List(std::slice::Iter<'a, (K, V)>),
480-
Map(std::collections::hash_map::Values<'a, K, V>),
478+
Map(hashbrown::hash_map::Values<'a, K, V>),
481479
}
482480

483481
impl<'a, K, V> Iterator for Values<'a, K, V> {
@@ -500,7 +498,7 @@ impl<'a, K, V> Iterator for Values<'a, K, V> {
500498

501499
pub enum ValuesMut<'a, K, V> {
502500
List(std::slice::IterMut<'a, (K, V)>),
503-
Map(std::collections::hash_map::ValuesMut<'a, K, V>),
501+
Map(hashbrown::hash_map::ValuesMut<'a, K, V>),
504502
}
505503

506504
impl<'a, K, V> Iterator for ValuesMut<'a, K, V> {
@@ -523,7 +521,7 @@ impl<'a, K, V> Iterator for ValuesMut<'a, K, V> {
523521

524522
pub enum IntoValues<K, V, const I: usize> {
525523
List(smallvec::IntoIter<[(K, V); I]>),
526-
Map(std::collections::hash_map::IntoValues<K, V>),
524+
Map(hashbrown::hash_map::IntoValues<K, V>),
527525
}
528526

529527
impl<K, V, const I: usize> Iterator for IntoValues<K, V, I> {
@@ -586,7 +584,7 @@ pub enum OccupiedEntry<'a, K, V, H, const I: usize> {
586584
},
587585
Map {
588586
this: *mut AutoMap<K, V, H, I>,
589-
entry: std::collections::hash_map::OccupiedEntry<'a, K, V>,
587+
entry: hashbrown::hash_map::OccupiedEntry<'a, K, V, H>,
590588
},
591589
}
592590

@@ -616,6 +614,20 @@ impl<K: Eq + Hash, V, H: BuildHasher + Default, const I: usize> OccupiedEntry<'_
616614
OccupiedEntry::Map { entry, this: _ } => entry.remove(),
617615
}
618616
}
617+
618+
pub fn replace_entry_with(self, func: impl FnOnce(&K, V) -> Option<V>) {
619+
match self {
620+
OccupiedEntry::List { list, index } => {
621+
let (key, value) = list.swap_remove(index);
622+
if let Some(value) = func(&key, value) {
623+
list.push((key, value));
624+
}
625+
}
626+
OccupiedEntry::Map { entry, .. } => {
627+
entry.replace_entry_with(func);
628+
}
629+
}
630+
}
619631
}
620632

621633
pub enum VacantEntry<'a, K, V, H, const I: usize> {
@@ -624,7 +636,7 @@ pub enum VacantEntry<'a, K, V, H, const I: usize> {
624636
list: &'a mut SmallVec<[(K, V); I]>,
625637
key: K,
626638
},
627-
Map(std::collections::hash_map::VacantEntry<'a, K, V>),
639+
Map(hashbrown::hash_map::VacantEntry<'a, K, V, H>),
628640
}
629641

630642
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default + 'a, const I: usize>
@@ -659,7 +671,7 @@ pub enum OccupiedRawEntry<'a, K, V, H, const I: usize> {
659671
},
660672
Map {
661673
this: *mut AutoMap<K, V, H, I>,
662-
entry: std::collections::hash_map::RawOccupiedEntryMut<'a, K, V, H>,
674+
entry: hashbrown::hash_map::RawOccupiedEntryMut<'a, K, V, H>,
663675
},
664676
}
665677

@@ -696,7 +708,7 @@ pub enum VacantRawEntry<'a, K, V, H, const I: usize> {
696708
this: *mut AutoMap<K, V, H, I>,
697709
list: &'a mut SmallVec<[(K, V); I]>,
698710
},
699-
Map(std::collections::hash_map::RawVacantEntryMut<'a, K, V, H>),
711+
Map(hashbrown::hash_map::RawVacantEntryMut<'a, K, V, H>),
700712
}
701713

702714
impl<'a, K: Eq + Hash, V, H: BuildHasher + Default + 'a, const I: usize>
@@ -862,7 +874,7 @@ where
862874
index: usize,
863875
f: F,
864876
},
865-
Map(std::collections::hash_map::ExtractIf<'l, K, V, F>),
877+
Map(hashbrown::hash_map::ExtractIf<'l, K, V, F>),
866878
}
867879

868880
impl<K, V, const I: usize, F> Iterator for ExtractIfIter<'_, K, V, I, F>
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use either::Either;
2+
use turbo_tasks::KeyValuePair;
3+
4+
use crate::data::{
5+
CachedDataItem, CachedDataItemKey, CachedDataItemStorage, CachedDataItemType,
6+
CachedDataItemValue, CachedDataItemValueRef, CachedDataItemValueRefMut,
7+
};
8+
9+
pub struct DynamicStorage {
10+
map: Vec<CachedDataItemStorage>,
11+
}
12+
13+
impl DynamicStorage {
14+
pub fn new() -> Self {
15+
Self {
16+
map: Default::default(),
17+
}
18+
}
19+
20+
fn get_or_create_map_mut(&mut self, ty: CachedDataItemType) -> &mut CachedDataItemStorage {
21+
let i = self.map.iter().position(|m| m.ty() == ty);
22+
if let Some(i) = i {
23+
&mut self.map[i]
24+
} else {
25+
self.map.reserve_exact(1);
26+
self.map.push(CachedDataItemStorage::new(ty));
27+
self.map.last_mut().unwrap()
28+
}
29+
}
30+
31+
fn get_map_mut(&mut self, ty: CachedDataItemType) -> Option<&mut CachedDataItemStorage> {
32+
self.map.iter_mut().find(|m| m.ty() == ty)
33+
}
34+
35+
fn get_map_index(&mut self, ty: CachedDataItemType) -> Option<usize> {
36+
self.map.iter_mut().position(|m| m.ty() == ty)
37+
}
38+
39+
fn get_or_create_map_index(&mut self, ty: CachedDataItemType) -> usize {
40+
let i = self.map.iter().position(|m| m.ty() == ty);
41+
if let Some(i) = i {
42+
i
43+
} else {
44+
let i = self.map.len();
45+
self.map.reserve_exact(1);
46+
self.map.push(CachedDataItemStorage::new(ty));
47+
i
48+
}
49+
}
50+
51+
fn get_map(&self, ty: CachedDataItemType) -> Option<&CachedDataItemStorage> {
52+
self.map.iter().find(|m| m.ty() == ty)
53+
}
54+
55+
pub fn add(&mut self, item: CachedDataItem) -> bool {
56+
let ty = item.ty();
57+
self.get_or_create_map_mut(ty).add(item)
58+
}
59+
60+
pub fn insert(&mut self, item: CachedDataItem) -> Option<CachedDataItemValue> {
61+
let ty = item.ty();
62+
self.get_or_create_map_mut(ty).insert(item)
63+
}
64+
65+
pub fn remove(&mut self, key: &CachedDataItemKey) -> Option<CachedDataItemValue> {
66+
self.get_map_index(key.ty()).and_then(|i| {
67+
let storage = &mut self.map[i];
68+
let result = storage.remove(key);
69+
if result.is_some() && storage.is_empty() {
70+
self.map.swap_remove(i);
71+
self.map.shrink_to_fit();
72+
}
73+
result
74+
})
75+
}
76+
77+
pub fn get(&self, key: &CachedDataItemKey) -> Option<CachedDataItemValueRef> {
78+
self.get_map(key.ty()).and_then(|m| m.get(key))
79+
}
80+
81+
pub fn get_mut(&mut self, key: &CachedDataItemKey) -> Option<CachedDataItemValueRefMut> {
82+
self.get_map_mut(key.ty()).and_then(|m| m.get_mut(key))
83+
}
84+
85+
pub fn get_mut_or_insert_with(
86+
&mut self,
87+
key: CachedDataItemKey,
88+
f: impl FnOnce() -> CachedDataItemValue,
89+
) -> CachedDataItemValueRefMut<'_> {
90+
self.get_or_create_map_mut(key.ty())
91+
.get_mut_or_insert_with(key, f)
92+
}
93+
94+
pub fn contains_key(&self, key: &CachedDataItemKey) -> bool {
95+
self.get_map(key.ty())
96+
.map(|m| m.contains_key(key))
97+
.unwrap_or_default()
98+
}
99+
100+
pub fn count(&self, ty: CachedDataItemType) -> usize {
101+
self.get_map(ty).map(|m| m.len()).unwrap_or_default()
102+
}
103+
104+
pub fn iter(
105+
&self,
106+
ty: CachedDataItemType,
107+
) -> impl Iterator<Item = (CachedDataItemKey, CachedDataItemValueRef<'_>)> {
108+
self.get_map(ty).map(|m| m.iter()).into_iter().flatten()
109+
}
110+
111+
pub fn iter_all(
112+
&self,
113+
) -> impl Iterator<Item = (CachedDataItemKey, CachedDataItemValueRef<'_>)> {
114+
self.map.iter().flat_map(|m| m.iter())
115+
}
116+
117+
pub fn extract_if<'l, F>(
118+
&'l mut self,
119+
ty: CachedDataItemType,
120+
mut f: F,
121+
) -> impl Iterator<Item = CachedDataItem> + use<'l, F>
122+
where
123+
F: for<'a> FnMut(CachedDataItemKey, CachedDataItemValueRef<'a>) -> bool + 'l,
124+
{
125+
// TODO this could be more efficient when the storage would support extract_if directly.
126+
// This requires some macro magic to make it work...
127+
// But we could potentially avoid the two temporary Vecs.
128+
let Some(i) = self.get_map_index(ty) else {
129+
return Either::Left(std::iter::empty());
130+
};
131+
let storage = &mut self.map[i];
132+
let items_to_extract = storage
133+
.iter()
134+
.filter(|(k, v)| f(*k, *v))
135+
.map(|(key, _)| key)
136+
.collect::<Vec<_>>();
137+
let items = items_to_extract
138+
.into_iter()
139+
.map(move |key| {
140+
let value = storage.remove(&key).unwrap();
141+
CachedDataItem::from_key_and_value(key, value)
142+
})
143+
.collect::<Vec<_>>();
144+
if self.map[i].is_empty() {
145+
self.map.swap_remove(i);
146+
self.map.shrink_to_fit();
147+
}
148+
Either::Right(items.into_iter())
149+
}
150+
151+
pub fn update(
152+
&mut self,
153+
key: CachedDataItemKey,
154+
update: impl FnOnce(Option<CachedDataItemValue>) -> Option<CachedDataItemValue>,
155+
) {
156+
let i = self.get_or_create_map_index(key.ty());
157+
let map = &mut self.map[i];
158+
map.update(key, update);
159+
if map.is_empty() {
160+
self.map.swap_remove(i);
161+
self.map.shrink_to_fit();
162+
}
163+
}
164+
165+
pub fn shrink_to_fit(&mut self, ty: CachedDataItemType) {
166+
if let Some(map) = self.get_map_mut(ty) {
167+
map.shrink_to_fit();
168+
}
169+
}
170+
}

turbopack/crates/turbo-tasks-backend/src/backend/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod dynamic_storage;
12
mod operation;
23
mod persisted_storage_log;
34
mod storage;

turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ pub trait TaskGuard: Debug {
372372
fn get_mut(&mut self, key: &CachedDataItemKey) -> Option<CachedDataItemValueRefMut<'_>>;
373373
fn get_mut_or_insert_with(
374374
&mut self,
375-
key: &CachedDataItemKey,
375+
key: CachedDataItemKey,
376376
insert: impl FnOnce() -> CachedDataItemValue,
377377
) -> CachedDataItemValueRefMut<'_>;
378378
fn has_key(&self, key: &CachedDataItemKey) -> bool;
@@ -603,7 +603,7 @@ impl<B: BackingStorage> TaskGuard for TaskGuardImpl<'_, B> {
603603

604604
fn get_mut_or_insert_with(
605605
&mut self,
606-
key: &CachedDataItemKey,
606+
key: CachedDataItemKey,
607607
insert: impl FnOnce() -> CachedDataItemValue,
608608
) -> CachedDataItemValueRefMut<'_> {
609609
self.check_access(key.category());
@@ -612,7 +612,7 @@ impl<B: BackingStorage> TaskGuard for TaskGuardImpl<'_, B> {
612612

613613
fn has_key(&self, key: &CachedDataItemKey) -> bool {
614614
self.check_access(key.category());
615-
self.task.has_key(key)
615+
self.task.contains_key(key)
616616
}
617617

618618
fn count(&self, ty: CachedDataItemType) -> usize {

0 commit comments

Comments
 (0)