Skip to content

Commit 373efff

Browse files
committed
Auto merge of #153826 - Zoxc:horde-maps, r=<try>
Use `horde`'s `SyncTable` for default query caches and `CtxtInterners`
2 parents d3cd040 + 873f282 commit 373efff

13 files changed

Lines changed: 284 additions & 72 deletions

File tree

Cargo.lock

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,14 @@ dependencies = [
17381738
"windows-sys 0.61.2",
17391739
]
17401740

1741+
[[package]]
1742+
name = "horde"
1743+
version = "0.1.4"
1744+
source = "git+https://github.com/Zoxc/horde.git?branch=test#8b3d4e2100e153e4e1abb5550978a3b5d136f9ff"
1745+
dependencies = [
1746+
"parking_lot",
1747+
]
1748+
17411749
[[package]]
17421750
name = "html-checker"
17431751
version = "0.1.0"
@@ -3862,6 +3870,7 @@ dependencies = [
38623870
"elsa",
38633871
"ena",
38643872
"hashbrown 0.17.0",
3873+
"horde",
38653874
"indexmap",
38663875
"jobserver",
38673876
"libc",

compiler/rustc_data_structures/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ bitflags = "2.4.1"
1010
either = "1.0"
1111
elsa = "1.11.0"
1212
ena = "0.14.3"
13+
horde = { version = "0.1.4", features = ["nightly"], git = "https://github.com/Zoxc/horde.git", branch = "test"}
1314
indexmap = "2.14.0"
1415
jobserver_crate = { version = "0.1.28", package = "jobserver" }
1516
measureme = "12.0.1"

compiler/rustc_data_structures/src/sharded.rs

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -209,60 +209,6 @@ impl<K: Eq + Hash, V> ShardedHashMap<K, V> {
209209
}
210210
}
211211

212-
impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
213-
#[inline]
214-
pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
215-
where
216-
K: Borrow<Q>,
217-
Q: Hash + Eq,
218-
{
219-
let hash = make_hash(value);
220-
let mut shard = self.lock_shard_by_hash(hash);
221-
222-
match table_entry(&mut shard, hash, value) {
223-
Entry::Occupied(e) => e.get().0,
224-
Entry::Vacant(e) => {
225-
let v = make();
226-
e.insert((v, ()));
227-
v
228-
}
229-
}
230-
}
231-
232-
#[inline]
233-
pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
234-
where
235-
K: Borrow<Q>,
236-
Q: Hash + Eq,
237-
{
238-
let hash = make_hash(&value);
239-
let mut shard = self.lock_shard_by_hash(hash);
240-
241-
match table_entry(&mut shard, hash, &value) {
242-
Entry::Occupied(e) => e.get().0,
243-
Entry::Vacant(e) => {
244-
let v = make(value);
245-
e.insert((v, ()));
246-
v
247-
}
248-
}
249-
}
250-
}
251-
252-
pub trait IntoPointer {
253-
/// Returns a pointer which outlives `self`.
254-
fn into_pointer(&self) -> *const ();
255-
}
256-
257-
impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
258-
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
259-
let hash = make_hash(&value);
260-
let shard = self.lock_shard_by_hash(hash);
261-
let value = value.into_pointer();
262-
shard.find(hash, |(k, ())| k.into_pointer() == value).is_some()
263-
}
264-
}
265-
266212
#[inline]
267213
pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
268214
let mut state = FxHasher::default();

compiler/rustc_data_structures/src/sync.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use std::collections::HashMap;
2626
use std::hash::{BuildHasher, Hash};
2727

28+
pub use horde::collect;
2829
pub use parking_lot::{
2930
MappedRwLockReadGuard as MappedReadGuard, MappedRwLockWriteGuard as MappedWriteGuard,
3031
RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard,
@@ -41,13 +42,15 @@ pub use self::parallel::{
4142
broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
4243
try_par_for_each_in,
4344
};
45+
pub use self::sync_table::{IntoPointer, LockedWrite, Read, SyncTable};
4446
pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec};
4547
pub use self::worker_local::{Registry, WorkerLocal};
4648
pub use crate::marker::*;
4749

4850
mod freeze;
4951
mod lock;
5052
mod parallel;
53+
mod sync_table;
5154
mod vec;
5255
mod worker_local;
5356

compiler/rustc_data_structures/src/sync/lock.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ impl<T> Lock<T> {
112112
self.data.get_mut()
113113
}
114114

115+
#[inline(always)]
116+
pub fn mode(&self) -> Mode {
117+
self.mode
118+
}
119+
115120
#[inline(always)]
116121
pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
117122
let mode = self.mode;
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
use std::borrow::Borrow;
2+
use std::hash::{BuildHasher, Hash, Hasher};
3+
use std::hint::cold_path;
4+
use std::ops::{Deref, DerefMut};
5+
6+
use horde::collect::{Pin, pin};
7+
pub use horde::sync_table::Read;
8+
use horde::sync_table::Write;
9+
use rustc_hash::FxBuildHasher;
10+
11+
use crate::sync::{DynSync, Lock, LockGuard, Mode};
12+
13+
pub struct SyncTable<K, V> {
14+
// We use this lock to protect `table` instead of the internal mutex in `horde::SyncTable`
15+
// as it's faster when synchronization is disabled.
16+
lock: Lock<()>,
17+
18+
table: horde::SyncTable<K, V, FxBuildHasher>,
19+
}
20+
21+
unsafe impl<K: DynSync, V: DynSync> DynSync for SyncTable<K, V> where FxBuildHasher: Sync {}
22+
23+
impl<K, V> Default for SyncTable<K, V> {
24+
fn default() -> Self {
25+
Self { lock: Lock::default(), table: horde::SyncTable::default() }
26+
}
27+
}
28+
29+
impl<K, V> SyncTable<K, V> {
30+
/// Creates a [Read] handle from a pinned region.
31+
///
32+
/// Use [horde::collect::pin] to get a `Pin` instance.
33+
#[inline]
34+
pub fn read<'a>(&'a self, pin: Pin<'a>) -> Read<'a, K, V, FxBuildHasher> {
35+
self.table.read(pin)
36+
}
37+
38+
/// Creates a [LockedWrite] handle by taking the underlying mutex that protects writes.
39+
#[inline]
40+
pub fn lock(&self) -> LockedWrite<'_, K, V> {
41+
LockedWrite {
42+
_guard: self.lock.lock(),
43+
table: {
44+
// SAFETY: We ensure there's only 1 writer at a time using our own lock
45+
unsafe { self.table.unsafe_write() }
46+
},
47+
}
48+
}
49+
50+
/// Creates a [LockedWrite] handle by taking the underlying mutex that protects writes.
51+
///
52+
/// This does not prune old tables unlike `lock`.
53+
#[inline]
54+
pub fn lock_no_prune(&self) -> LockedWrite<'_, K, V> {
55+
LockedWrite {
56+
_guard: self.lock.lock(),
57+
table: {
58+
// SAFETY: We ensure there's only 1 writer at a time using our own lock
59+
unsafe { self.table.unsafe_write_no_prune() }
60+
},
61+
}
62+
}
63+
64+
/// Hashes a key with the table's hasher.
65+
#[inline]
66+
pub fn hash_key<Q>(&self, key: &Q) -> u64
67+
where
68+
K: Borrow<Q>,
69+
Q: ?Sized + Hash,
70+
{
71+
self.table.hash_key::<Q>(key)
72+
}
73+
74+
pub fn len(&self) -> usize {
75+
pin(|pin| self.read(pin).len())
76+
}
77+
78+
pub fn with_capacity(cap: usize) -> Self {
79+
Self { lock: Lock::new(()), table: horde::SyncTable::new_with(FxBuildHasher, cap) }
80+
}
81+
}
82+
83+
/// A handle to a [SyncTable] with write access protected by a lock.
84+
pub struct LockedWrite<'a, K, V> {
85+
table: Write<'a, K, V, FxBuildHasher>,
86+
_guard: LockGuard<'a, ()>,
87+
}
88+
89+
impl<'a, K, V> Deref for LockedWrite<'a, K, V> {
90+
type Target = Write<'a, K, V, FxBuildHasher>;
91+
92+
#[inline]
93+
fn deref(&self) -> &Self::Target {
94+
&self.table
95+
}
96+
}
97+
98+
impl<'a, K, V> DerefMut for LockedWrite<'a, K, V> {
99+
#[inline]
100+
fn deref_mut(&mut self) -> &mut Self::Target {
101+
&mut self.table
102+
}
103+
}
104+
105+
pub trait IntoPointer {
106+
/// Returns a pointer which outlives `self`.
107+
fn into_pointer(&self) -> *const ();
108+
}
109+
110+
impl<K: Eq + Hash + Copy> SyncTable<K, ()> {
111+
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool
112+
where
113+
K: IntoPointer,
114+
{
115+
pin(|pin| {
116+
let mut state = FxBuildHasher.build_hasher();
117+
value.hash(&mut state);
118+
let hash = state.finish();
119+
let value = value.into_pointer();
120+
self.read(pin).get_from_hash(hash, |entry| entry.into_pointer() == value).is_some()
121+
})
122+
}
123+
124+
#[inline]
125+
pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
126+
where
127+
K: Borrow<Q>,
128+
Q: Hash + Eq,
129+
{
130+
if self.lock.mode() == Mode::Sync {
131+
pin(|pin| {
132+
let hash = self.hash_key(value);
133+
134+
let potential = match self.read(pin).get_potential(&value, Some(hash)) {
135+
Ok(entry) => return *entry.0,
136+
Err(potential) => {
137+
cold_path();
138+
potential
139+
}
140+
};
141+
142+
let mut write = self.lock();
143+
144+
let potential = match potential.refresh(self.read(pin), &value, Some(hash)) {
145+
Ok(entry) => {
146+
cold_path();
147+
return *entry.0;
148+
}
149+
Err(potential) => potential,
150+
};
151+
152+
let result = make();
153+
154+
potential.insert_new(&mut write, result, (), Some(hash));
155+
156+
result
157+
})
158+
} else {
159+
let mut write = self.lock_no_prune();
160+
161+
let hash = self.hash_key(&value);
162+
163+
let entry = write.read().get(&value, Some(hash));
164+
if let Some(entry) = entry {
165+
return *entry.0;
166+
}
167+
write.prune();
168+
169+
let result = make();
170+
171+
write.insert_new(result, (), Some(hash));
172+
173+
result
174+
}
175+
}
176+
177+
#[inline]
178+
pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K
179+
where
180+
K: Borrow<Q>,
181+
Q: Hash + Eq,
182+
{
183+
if self.lock.mode() == Mode::Sync {
184+
pin(|pin| {
185+
let hash = self.hash_key(&value);
186+
187+
let potential = match self.read(pin).get_potential(&value, Some(hash)) {
188+
Ok(entry) => return *entry.0,
189+
Err(potential) => {
190+
cold_path();
191+
potential
192+
}
193+
};
194+
195+
let mut write = self.lock();
196+
197+
let potential = match potential.refresh(self.read(pin), &value, Some(hash)) {
198+
Ok(entry) => {
199+
cold_path();
200+
return *entry.0;
201+
}
202+
Err(potential) => potential,
203+
};
204+
205+
let result = make(value);
206+
207+
potential.insert_new(&mut write, result, (), Some(hash));
208+
209+
result
210+
})
211+
} else {
212+
let mut write = self.lock_no_prune();
213+
214+
let hash = self.hash_key(&value);
215+
216+
let entry = write.read().get(&value, Some(hash));
217+
if let Some(entry) = entry {
218+
return *entry.0;
219+
}
220+
write.prune();
221+
222+
let result = make(value);
223+
224+
write.insert_new(result, (), Some(hash));
225+
226+
result
227+
}
228+
}
229+
}

compiler/rustc_interface/src/util.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig};
1616
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
1717
use rustc_data_structures::fx::FxIndexMap;
1818
use rustc_data_structures::jobserver::Proxy;
19-
use rustc_data_structures::sync;
19+
use rustc_data_structures::sync::{self, collect};
2020
use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib};
2121
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
2222
use rustc_middle::ty::{CurrentGcx, TyCtxt};
@@ -217,7 +217,10 @@ pub(crate) fn run_in_thread_pool_with_globals<
217217
let builder = rustc_thread_pool::ThreadPoolBuilder::new()
218218
.thread_name(|_| "rustc".to_string())
219219
.acquire_thread_handler(move || proxy_.acquire_thread())
220-
.release_thread_handler(move || proxy__.release_thread())
220+
.release_thread_handler(move || {
221+
collect::release();
222+
proxy__.release_thread()
223+
})
221224
.num_threads(threads)
222225
.deadlock_handler(move || {
223226
// On deadlock, creates a new thread and forwards information in thread

0 commit comments

Comments
 (0)