|
41 | 41 |
|
42 | 42 | use std::cell::RefCell; |
43 | 43 | use std::cmp::max; |
44 | | -use std::sync::Arc; |
45 | 44 | use std::sync::atomic::Ordering; |
| 45 | +use std::sync::{Arc, OnceLock}; |
46 | 46 | use std::{iter, mem}; |
47 | 47 |
|
48 | 48 | use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; |
@@ -113,14 +113,51 @@ pub struct SerializedDepGraph { |
113 | 113 | /// A flattened list of all edge targets in the graph, stored in the same |
114 | 114 | /// varint encoding that we use on disk. Edge sources are implicit in edge_list_indices. |
115 | 115 | edge_list_data: Vec<u8>, |
116 | | - /// For each dep kind, stores a map from key fingerprints back to the index |
117 | | - /// of the corresponding node. This is the inverse of `nodes`. |
118 | | - index: Vec<UnhashMap<PackedFingerprint, SerializedDepNodeIndex>>, |
| 116 | + /// The lazily-built inverse of `nodes`: maps a [`DepNode`] back to its |
| 117 | + /// [`SerializedDepNodeIndex`] via the node's key fingerprint. See |
| 118 | + /// [`LazyNodeIndex`]. |
| 119 | + index: LazyNodeIndex, |
119 | 120 | /// The number of previous compilation sessions. This is used to generate |
120 | 121 | /// unique anon dep nodes per session. |
121 | 122 | session_count: u64, |
122 | 123 | } |
123 | 124 |
|
| 125 | +/// The inverse of [`SerializedDepGraph::nodes`], built lazily per [`DepKind`]. |
| 126 | +/// |
| 127 | +/// The node indices are grouped by `DepKind` into contiguous ranges of |
| 128 | +/// `nodes_by_kind` when the graph is decoded (a cheap counting sort). The actual |
| 129 | +/// fingerprint -> index map for a given `DepKind` is only built the first time a |
| 130 | +/// node of that kind is looked up by fingerprint. |
| 131 | +/// |
| 132 | +/// This matters for incremental performance: marking the graph green in an |
| 133 | +/// unchanged session reaches the overwhelming majority of nodes by walking |
| 134 | +/// edges, which reference nodes by index (see `try_mark_previous_green`), not by |
| 135 | +/// fingerprint. Only the comparatively few nodes that the session queries |
| 136 | +/// directly are ever looked up here, and those cluster into a handful of |
| 137 | +/// `DepKind`s. Building a map for every kind up front (the bulk being per-`DefId` |
| 138 | +/// foreign/metadata reads that are never looked up) is therefore mostly wasted |
| 139 | +/// work. |
| 140 | +#[derive(Debug, Default)] |
| 141 | +struct LazyNodeIndex { |
| 142 | + /// All (non-`Null`) node indices, grouped into contiguous per-`DepKind` |
| 143 | + /// ranges described by `kinds`. |
| 144 | + nodes_by_kind: Vec<SerializedDepNodeIndex>, |
| 145 | + /// For each `DepKind`, the range of `nodes_by_kind` holding its node indices |
| 146 | + /// and the lazily-built fingerprint map over that range. |
| 147 | + kinds: Vec<LazyKindIndex>, |
| 148 | +} |
| 149 | + |
| 150 | +#[derive(Debug, Default)] |
| 151 | +struct LazyKindIndex { |
| 152 | + /// Offset into `LazyNodeIndex::nodes_by_kind` of this kind's first node. |
| 153 | + start: u32, |
| 154 | + /// Number of nodes of this kind. |
| 155 | + len: u32, |
| 156 | + /// `key_fingerprint -> node index`, built from this kind's range on first |
| 157 | + /// lookup. Empty kinds (and kinds never looked up) never build a map. |
| 158 | + map: OnceLock<UnhashMap<PackedFingerprint, SerializedDepNodeIndex>>, |
| 159 | +} |
| 160 | + |
124 | 161 | impl SerializedDepGraph { |
125 | 162 | #[inline] |
126 | 163 | pub fn edge_targets_from( |
@@ -151,7 +188,29 @@ impl SerializedDepGraph { |
151 | 188 |
|
152 | 189 | #[inline] |
153 | 190 | pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option<SerializedDepNodeIndex> { |
154 | | - self.index.get(dep_node.kind.as_usize())?.get(&dep_node.key_fingerprint).copied() |
| 191 | + let kind = self.index.kinds.get(dep_node.kind.as_usize())?; |
| 192 | + let map = kind.map.get_or_init(|| { |
| 193 | + let range = (kind.start as usize)..(kind.start as usize + kind.len as usize); |
| 194 | + let mut map = UnhashMap::with_capacity_and_hasher(kind.len as usize, Default::default()); |
| 195 | + for &idx in &self.index.nodes_by_kind[range] { |
| 196 | + let node = &self.nodes[idx]; |
| 197 | + if map.insert(node.key_fingerprint, idx).is_some() |
| 198 | + // Side effect nodes can legitimately share a fingerprint. |
| 199 | + && node.kind != DepKind::SideEffect |
| 200 | + { |
| 201 | + panic!( |
| 202 | + "Error: A dep graph node ({:?}) does not have an unique index. \ |
| 203 | + Running a clean build on a nightly compiler with \ |
| 204 | + `-Z incremental-verify-ich` can help narrow down the issue for reporting. \ |
| 205 | + A clean build may also work around the issue.\n |
| 206 | + DepNode: {node:?}", |
| 207 | + node.kind |
| 208 | + ) |
| 209 | + } |
| 210 | + } |
| 211 | + map |
| 212 | + }); |
| 213 | + map.get(&dep_node.key_fingerprint).copied() |
155 | 214 | } |
156 | 215 |
|
157 | 216 | #[inline] |
@@ -286,28 +345,36 @@ impl SerializedDepGraph { |
286 | 345 | // end of the array. This padding ensure it doesn't. |
287 | 346 | edge_list_data.extend(&[0u8; DEP_NODE_PAD]); |
288 | 347 |
|
289 | | - // Read the number of each dep kind and use it to create an hash map with a suitable size. |
290 | | - let mut index: Vec<_> = (0..(DepKind::MAX + 1)) |
291 | | - .map(|_| UnhashMap::with_capacity_and_hasher(d.read_u32() as usize, Default::default())) |
292 | | - .collect(); |
| 348 | + // Read the number of nodes of each dep kind. These per-kind counts let us |
| 349 | + // group node indices by kind below; the fingerprint -> index maps |
| 350 | + // themselves are built lazily, per kind, on first lookup (see |
| 351 | + // `LazyNodeIndex`). |
| 352 | + let kind_counts: Vec<u32> = (0..(DepKind::MAX + 1)).map(|_| d.read_u32()).collect(); |
293 | 353 |
|
294 | 354 | let session_count = d.read_u64(); |
295 | 355 |
|
| 356 | + // Counting sort: place every (non-`Null`) node index into a contiguous |
| 357 | + // per-kind range of `nodes_by_kind`, using the per-kind counts to compute |
| 358 | + // each kind's start offset. Padding gaps (`DepKind::Null`) are never |
| 359 | + // looked up by fingerprint, so we skip them. |
| 360 | + let mut kinds = Vec::with_capacity(kind_counts.len()); |
| 361 | + let mut offset = 0u32; |
| 362 | + for &len in &kind_counts { |
| 363 | + kinds.push(LazyKindIndex { start: offset, len, map: OnceLock::new() }); |
| 364 | + offset += len; |
| 365 | + } |
| 366 | + debug_assert_eq!(offset as usize, node_count); |
| 367 | + let mut nodes_by_kind = vec![SerializedDepNodeIndex::from_u32(0); offset as usize]; |
| 368 | + let mut fill: Vec<u32> = kinds.iter().map(|k| k.start).collect(); |
296 | 369 | for (idx, node) in nodes.iter_enumerated() { |
297 | | - if index[node.kind.as_usize()].insert(node.key_fingerprint, idx).is_some() { |
298 | | - // Empty nodes and side effect nodes can have duplicates |
299 | | - if node.kind != DepKind::Null && node.kind != DepKind::SideEffect { |
300 | | - let kind = node.kind; |
301 | | - panic!( |
302 | | - "Error: A dep graph node ({kind:?}) does not have an unique index. \ |
303 | | - Running a clean build on a nightly compiler with \ |
304 | | - `-Z incremental-verify-ich` can help narrow down the issue for reporting. \ |
305 | | - A clean build may also work around the issue.\n |
306 | | - DepNode: {node:?}" |
307 | | - ) |
308 | | - } |
| 370 | + if node.kind == DepKind::Null { |
| 371 | + continue; |
309 | 372 | } |
| 373 | + let k = node.kind.as_usize(); |
| 374 | + nodes_by_kind[fill[k] as usize] = idx; |
| 375 | + fill[k] += 1; |
310 | 376 | } |
| 377 | + let index = LazyNodeIndex { nodes_by_kind, kinds }; |
311 | 378 |
|
312 | 379 | Arc::new(SerializedDepGraph { |
313 | 380 | nodes, |
|
0 commit comments