Skip to content

Commit

Permalink
[Turbopack] Inline commonly used storages (#75284)
Browse files Browse the repository at this point in the history
### What?

* move code from macro into generic struct
* move dynamic storage access into separate struct
* generate inline storage for common data
  • Loading branch information
sokra authored Jan 24, 2025
1 parent 111f334 commit 47a0cd2
Show file tree
Hide file tree
Showing 11 changed files with 887 additions and 489 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions turbopack/crates/turbo-tasks-auto-hash-map/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ edition = "2021"
workspace = true

[dependencies]
hashbrown = { workspace = true, features = ["serde"]}
rustc-hash = { workspace = true }
serde = { workspace = true, features = ["derive"] }
smallvec = { workspace = true }
48 changes: 30 additions & 18 deletions turbopack/crates/turbo-tasks-auto-hash-map/src/map.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::{
borrow::Borrow,
collections::HashMap,
fmt::{Debug, Formatter},
hash::{BuildHasher, BuildHasherDefault, Hash},
marker::PhantomData,
};

use hashbrown::hash_map::HashMap;
use rustc_hash::FxHasher;
use serde::{
de::{MapAccess, Visitor},
Expand Down Expand Up @@ -176,12 +176,10 @@ impl<K: Eq + Hash, V, H: BuildHasher + Default, const I: usize> AutoMap<K, V, H,
None => Entry::Vacant(VacantEntry::List { this, list, key }),
},
AutoMap::Map(map) => match map.entry(key) {
std::collections::hash_map::Entry::Occupied(entry) => {
hashbrown::hash_map::Entry::Occupied(entry) => {
Entry::Occupied(OccupiedEntry::Map { this, entry })
}
std::collections::hash_map::Entry::Vacant(entry) => {
Entry::Vacant(VacantEntry::Map(entry))
}
hashbrown::hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry::Map(entry)),
},
}
}
Expand All @@ -198,10 +196,10 @@ impl<K: Eq + Hash, V, H: BuildHasher + Default, const I: usize> AutoMap<K, V, H,
None => RawEntry::Vacant(VacantRawEntry::List { this, list }),
},
AutoMap::Map(map) => match map.raw_entry_mut().from_key(key) {
std::collections::hash_map::RawEntryMut::Occupied(entry) => {
hashbrown::hash_map::RawEntryMut::Occupied(entry) => {
RawEntry::Occupied(OccupiedRawEntry::Map { this, entry })
}
std::collections::hash_map::RawEntryMut::Vacant(entry) => {
hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
RawEntry::Vacant(VacantRawEntry::Map(entry))
}
},
Expand Down Expand Up @@ -397,7 +395,7 @@ impl<'a, K, V, H, const I: usize> IntoIterator for &'a AutoMap<K, V, H, I> {

pub enum Iter<'a, K, V> {
List(std::slice::Iter<'a, (K, V)>),
Map(std::collections::hash_map::Iter<'a, K, V>),
Map(hashbrown::hash_map::Iter<'a, K, V>),
}

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

pub enum IterMut<'a, K, V> {
List(std::slice::IterMut<'a, (K, V)>),
Map(std::collections::hash_map::IterMut<'a, K, V>),
Map(hashbrown::hash_map::IterMut<'a, K, V>),
}

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

pub enum IntoIter<K, V, const I: usize> {
List(smallvec::IntoIter<[(K, V); I]>),
Map(std::collections::hash_map::IntoIter<K, V>),
Map(hashbrown::hash_map::IntoIter<K, V>),
}

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

pub enum Values<'a, K, V> {
List(std::slice::Iter<'a, (K, V)>),
Map(std::collections::hash_map::Values<'a, K, V>),
Map(hashbrown::hash_map::Values<'a, K, V>),
}

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

pub enum ValuesMut<'a, K, V> {
List(std::slice::IterMut<'a, (K, V)>),
Map(std::collections::hash_map::ValuesMut<'a, K, V>),
Map(hashbrown::hash_map::ValuesMut<'a, K, V>),
}

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

pub enum IntoValues<K, V, const I: usize> {
List(smallvec::IntoIter<[(K, V); I]>),
Map(std::collections::hash_map::IntoValues<K, V>),
Map(hashbrown::hash_map::IntoValues<K, V>),
}

impl<K, V, const I: usize> Iterator for IntoValues<K, V, I> {
Expand Down Expand Up @@ -586,7 +584,7 @@ pub enum OccupiedEntry<'a, K, V, H, const I: usize> {
},
Map {
this: *mut AutoMap<K, V, H, I>,
entry: std::collections::hash_map::OccupiedEntry<'a, K, V>,
entry: hashbrown::hash_map::OccupiedEntry<'a, K, V, H>,
},
}

Expand Down Expand Up @@ -616,6 +614,20 @@ impl<K: Eq + Hash, V, H: BuildHasher + Default, const I: usize> OccupiedEntry<'_
OccupiedEntry::Map { entry, this: _ } => entry.remove(),
}
}

pub fn replace_entry_with(self, func: impl FnOnce(&K, V) -> Option<V>) {
match self {
OccupiedEntry::List { list, index } => {
let (key, value) = list.swap_remove(index);
if let Some(value) = func(&key, value) {
list.push((key, value));
}
}
OccupiedEntry::Map { entry, .. } => {
entry.replace_entry_with(func);
}
}
}
}

pub enum VacantEntry<'a, K, V, H, const I: usize> {
Expand All @@ -624,7 +636,7 @@ pub enum VacantEntry<'a, K, V, H, const I: usize> {
list: &'a mut SmallVec<[(K, V); I]>,
key: K,
},
Map(std::collections::hash_map::VacantEntry<'a, K, V>),
Map(hashbrown::hash_map::VacantEntry<'a, K, V, H>),
}

impl<'a, K: Eq + Hash, V, H: BuildHasher + Default + 'a, const I: usize>
Expand Down Expand Up @@ -659,7 +671,7 @@ pub enum OccupiedRawEntry<'a, K, V, H, const I: usize> {
},
Map {
this: *mut AutoMap<K, V, H, I>,
entry: std::collections::hash_map::RawOccupiedEntryMut<'a, K, V, H>,
entry: hashbrown::hash_map::RawOccupiedEntryMut<'a, K, V, H>,
},
}

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

impl<'a, K: Eq + Hash, V, H: BuildHasher + Default + 'a, const I: usize>
Expand Down Expand Up @@ -862,7 +874,7 @@ where
index: usize,
f: F,
},
Map(std::collections::hash_map::ExtractIf<'l, K, V, F>),
Map(hashbrown::hash_map::ExtractIf<'l, K, V, F>),
}

impl<K, V, const I: usize, F> Iterator for ExtractIfIter<'_, K, V, I, F>
Expand Down
170 changes: 170 additions & 0 deletions turbopack/crates/turbo-tasks-backend/src/backend/dynamic_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
use either::Either;
use turbo_tasks::KeyValuePair;

use crate::data::{
CachedDataItem, CachedDataItemKey, CachedDataItemStorage, CachedDataItemType,
CachedDataItemValue, CachedDataItemValueRef, CachedDataItemValueRefMut,
};

pub struct DynamicStorage {
map: Vec<CachedDataItemStorage>,
}

impl DynamicStorage {
pub fn new() -> Self {
Self {
map: Default::default(),
}
}

fn get_or_create_map_mut(&mut self, ty: CachedDataItemType) -> &mut CachedDataItemStorage {
let i = self.map.iter().position(|m| m.ty() == ty);
if let Some(i) = i {
&mut self.map[i]
} else {
self.map.reserve_exact(1);
self.map.push(CachedDataItemStorage::new(ty));
self.map.last_mut().unwrap()
}
}

fn get_map_mut(&mut self, ty: CachedDataItemType) -> Option<&mut CachedDataItemStorage> {
self.map.iter_mut().find(|m| m.ty() == ty)
}

fn get_map_index(&mut self, ty: CachedDataItemType) -> Option<usize> {
self.map.iter_mut().position(|m| m.ty() == ty)
}

fn get_or_create_map_index(&mut self, ty: CachedDataItemType) -> usize {
let i = self.map.iter().position(|m| m.ty() == ty);
if let Some(i) = i {
i
} else {
let i = self.map.len();
self.map.reserve_exact(1);
self.map.push(CachedDataItemStorage::new(ty));
i
}
}

fn get_map(&self, ty: CachedDataItemType) -> Option<&CachedDataItemStorage> {
self.map.iter().find(|m| m.ty() == ty)
}

pub fn add(&mut self, item: CachedDataItem) -> bool {
let ty = item.ty();
self.get_or_create_map_mut(ty).add(item)
}

pub fn insert(&mut self, item: CachedDataItem) -> Option<CachedDataItemValue> {
let ty = item.ty();
self.get_or_create_map_mut(ty).insert(item)
}

pub fn remove(&mut self, key: &CachedDataItemKey) -> Option<CachedDataItemValue> {
self.get_map_index(key.ty()).and_then(|i| {
let storage = &mut self.map[i];
let result = storage.remove(key);
if result.is_some() && storage.is_empty() {
self.map.swap_remove(i);
self.map.shrink_to_fit();
}
result
})
}

pub fn get(&self, key: &CachedDataItemKey) -> Option<CachedDataItemValueRef> {
self.get_map(key.ty()).and_then(|m| m.get(key))
}

pub fn get_mut(&mut self, key: &CachedDataItemKey) -> Option<CachedDataItemValueRefMut> {
self.get_map_mut(key.ty()).and_then(|m| m.get_mut(key))
}

pub fn get_mut_or_insert_with(
&mut self,
key: CachedDataItemKey,
f: impl FnOnce() -> CachedDataItemValue,
) -> CachedDataItemValueRefMut<'_> {
self.get_or_create_map_mut(key.ty())
.get_mut_or_insert_with(key, f)
}

pub fn contains_key(&self, key: &CachedDataItemKey) -> bool {
self.get_map(key.ty())
.map(|m| m.contains_key(key))
.unwrap_or_default()
}

pub fn count(&self, ty: CachedDataItemType) -> usize {
self.get_map(ty).map(|m| m.len()).unwrap_or_default()
}

pub fn iter(
&self,
ty: CachedDataItemType,
) -> impl Iterator<Item = (CachedDataItemKey, CachedDataItemValueRef<'_>)> {
self.get_map(ty).map(|m| m.iter()).into_iter().flatten()
}

pub fn iter_all(
&self,
) -> impl Iterator<Item = (CachedDataItemKey, CachedDataItemValueRef<'_>)> {
self.map.iter().flat_map(|m| m.iter())
}

pub fn extract_if<'l, F>(
&'l mut self,
ty: CachedDataItemType,
mut f: F,
) -> impl Iterator<Item = CachedDataItem> + use<'l, F>
where
F: for<'a> FnMut(CachedDataItemKey, CachedDataItemValueRef<'a>) -> bool + 'l,
{
// TODO this could be more efficient when the storage would support extract_if directly.
// This requires some macro magic to make it work...
// But we could potentially avoid the two temporary Vecs.
let Some(i) = self.get_map_index(ty) else {
return Either::Left(std::iter::empty());
};
let storage = &mut self.map[i];
let items_to_extract = storage
.iter()
.filter(|(k, v)| f(*k, *v))
.map(|(key, _)| key)
.collect::<Vec<_>>();
let items = items_to_extract
.into_iter()
.map(move |key| {
let value = storage.remove(&key).unwrap();
CachedDataItem::from_key_and_value(key, value)
})
.collect::<Vec<_>>();
if self.map[i].is_empty() {
self.map.swap_remove(i);
self.map.shrink_to_fit();
}
Either::Right(items.into_iter())
}

pub fn update(
&mut self,
key: CachedDataItemKey,
update: impl FnOnce(Option<CachedDataItemValue>) -> Option<CachedDataItemValue>,
) {
let i = self.get_or_create_map_index(key.ty());
let map = &mut self.map[i];
map.update(key, update);
if map.is_empty() {
self.map.swap_remove(i);
self.map.shrink_to_fit();
}
}

pub fn shrink_to_fit(&mut self, ty: CachedDataItemType) {
if let Some(map) = self.get_map_mut(ty) {
map.shrink_to_fit();
}
}
}
1 change: 1 addition & 0 deletions turbopack/crates/turbo-tasks-backend/src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod dynamic_storage;
mod operation;
mod persisted_storage_log;
mod storage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ pub trait TaskGuard: Debug {
fn get_mut(&mut self, key: &CachedDataItemKey) -> Option<CachedDataItemValueRefMut<'_>>;
fn get_mut_or_insert_with(
&mut self,
key: &CachedDataItemKey,
key: CachedDataItemKey,
insert: impl FnOnce() -> CachedDataItemValue,
) -> CachedDataItemValueRefMut<'_>;
fn has_key(&self, key: &CachedDataItemKey) -> bool;
Expand Down Expand Up @@ -603,7 +603,7 @@ impl<B: BackingStorage> TaskGuard for TaskGuardImpl<'_, B> {

fn get_mut_or_insert_with(
&mut self,
key: &CachedDataItemKey,
key: CachedDataItemKey,
insert: impl FnOnce() -> CachedDataItemValue,
) -> CachedDataItemValueRefMut<'_> {
self.check_access(key.category());
Expand All @@ -612,7 +612,7 @@ impl<B: BackingStorage> TaskGuard for TaskGuardImpl<'_, B> {

fn has_key(&self, key: &CachedDataItemKey) -> bool {
self.check_access(key.category());
self.task.has_key(key)
self.task.contains_key(key)
}

fn count(&self, ty: CachedDataItemType) -> usize {
Expand Down
Loading

0 comments on commit 47a0cd2

Please sign in to comment.