Skip to content

Commit 20be77e

Browse files
committed
Address review: rename field, lazy-build helper, fold decode loops
Rename the reverse-index field from `index` to `reverse_index`. Move the per-kind fingerprint map build into `LazyKindIndex::fingerprint_map` and have it `debug_assert` each node's kind matches the kind being built, so `node_to_index_opt` is just the lookup. Read the per-kind counts and build their offset ranges in a single decode loop, and store the grouped indices as `Option<SerializedDepNodeIndex>` (sized by `node_count`) so an unfilled slot is `None` rather than a real-looking index; the counting sort fills every slot of each range exactly once. Document why `Null` nodes are skipped.
1 parent 36661b1 commit 20be77e

1 file changed

Lines changed: 71 additions & 44 deletions

File tree

compiler/rustc_middle/src/dep_graph/serialized.rs

Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use rustc_data_structures::outline;
5151
use rustc_data_structures::profiling::SelfProfilerRef;
5252
use rustc_data_structures::sync::{AtomicU64, Lock, WorkerLocal, broadcast};
5353
use rustc_data_structures::unhash::UnhashMap;
54-
use rustc_index::IndexVec;
54+
use rustc_index::{IndexSlice, IndexVec};
5555
use rustc_serialize::opaque::mem_encoder::MemEncoder;
5656
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder};
5757
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@@ -116,7 +116,7 @@ pub struct SerializedDepGraph {
116116
/// The lazily-built inverse of `nodes`: maps a [`DepNode`] back to its
117117
/// [`SerializedDepNodeIndex`] via the node's key fingerprint. See
118118
/// [`LazyNodeIndex`].
119-
index: LazyNodeIndex,
119+
reverse_index: LazyNodeIndex,
120120
/// The number of previous compilation sessions. This is used to generate
121121
/// unique anon dep nodes per session.
122122
session_count: u64,
@@ -140,8 +140,11 @@ pub struct SerializedDepGraph {
140140
#[derive(Debug, Default)]
141141
struct LazyNodeIndex {
142142
/// All (non-`Null`) node indices, grouped into contiguous per-`DepKind`
143-
/// ranges described by `kinds`.
144-
nodes_by_kind: Vec<SerializedDepNodeIndex>,
143+
/// ranges described by `kinds`. Allocated with `None` in every slot; the
144+
/// counting sort in `decode` fills each slot of every range exactly once, so a
145+
/// `None` is never read back. `SerializedDepNodeIndex` has a niche, so the
146+
/// `Option` does not grow the vector.
147+
nodes_by_kind: Vec<Option<SerializedDepNodeIndex>>,
145148
/// For each `DepKind`, the range of `nodes_by_kind` holding its node indices
146149
/// and the lazily-built fingerprint map over that range.
147150
kinds: Vec<LazyKindIndex>,
@@ -158,6 +161,43 @@ struct LazyKindIndex {
158161
map: OnceLock<UnhashMap<PackedFingerprint, SerializedDepNodeIndex>>,
159162
}
160163

164+
impl LazyKindIndex {
165+
/// Returns this kind's `key_fingerprint -> node index` map, building it from
166+
/// the kind's range of `nodes_by_kind` on the first call. Building does the
167+
/// per-node hash-map inserts that decode used to perform eagerly for every
168+
/// kind; here we only pay it for kinds that are actually looked up.
169+
fn fingerprint_map(
170+
&self,
171+
kind: DepKind,
172+
nodes: &IndexSlice<SerializedDepNodeIndex, DepNode>,
173+
nodes_by_kind: &[Option<SerializedDepNodeIndex>],
174+
) -> &UnhashMap<PackedFingerprint, SerializedDepNodeIndex> {
175+
self.map.get_or_init(|| {
176+
let range = (self.start as usize)..(self.start as usize + self.len as usize);
177+
let mut map =
178+
UnhashMap::with_capacity_and_hasher(self.len as usize, Default::default());
179+
for &idx in &nodes_by_kind[range] {
180+
let idx = idx.expect("counting sort fills every slot of a kind's range");
181+
let node = &nodes[idx];
182+
debug_assert_eq!(node.kind, kind);
183+
if map.insert(node.key_fingerprint, idx).is_some()
184+
// Side effect nodes can legitimately share a fingerprint.
185+
&& node.kind != DepKind::SideEffect
186+
{
187+
panic!(
188+
"Error: A dep graph node ({kind:?}) does not have an unique index. \
189+
Running a clean build on a nightly compiler with \
190+
`-Z incremental-verify-ich` can help narrow down the issue for reporting. \
191+
A clean build may also work around the issue.\n
192+
DepNode: {node:?}"
193+
)
194+
}
195+
}
196+
map
197+
})
198+
}
199+
}
200+
161201
impl SerializedDepGraph {
162202
#[inline]
163203
pub fn edge_targets_from(
@@ -188,28 +228,9 @@ impl SerializedDepGraph {
188228

189229
#[inline]
190230
pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option<SerializedDepNodeIndex> {
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-
});
231+
let kind = self.reverse_index.kinds.get(dep_node.kind.as_usize())?;
232+
let map =
233+
kind.fingerprint_map(dep_node.kind, &self.nodes, &self.reverse_index.nodes_by_kind);
213234
map.get(&dep_node.key_fingerprint).copied()
214235
}
215236

@@ -345,43 +366,49 @@ impl SerializedDepGraph {
345366
// end of the array. This padding ensure it doesn't.
346367
edge_list_data.extend(&[0u8; DEP_NODE_PAD]);
347368

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();
353-
354-
let session_count = d.read_u64();
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());
369+
// Read the number of nodes of each dep kind, turning each per-kind count
370+
// into a contiguous, still-empty range of `nodes_by_kind` as we go (a
371+
// prefix sum of the counts gives each kind's start offset). The
372+
// fingerprint -> index map for a kind is built lazily, on the first lookup
373+
// of that kind (see `LazyKindIndex::fingerprint_map`).
374+
let mut kinds = Vec::with_capacity(DepKind::MAX as usize + 1);
361375
let mut offset = 0u32;
362-
for &len in &kind_counts {
376+
for _ in 0..(DepKind::MAX + 1) {
377+
let len = d.read_u32();
363378
kinds.push(LazyKindIndex { start: offset, len, map: OnceLock::new() });
364379
offset += len;
365380
}
381+
// Every encoded node has a (non-`Null`) kind, so the per-kind counts sum to
382+
// `node_count`; `Null` nodes are never encoded, so no range is reserved for
383+
// them.
366384
debug_assert_eq!(offset as usize, node_count);
367-
let mut nodes_by_kind = vec![SerializedDepNodeIndex::from_u32(0); offset as usize];
385+
386+
let session_count = d.read_u64();
387+
388+
// Counting sort: place each node index into its kind's range. `fill[k]`
389+
// points at the next free slot in kind `k`'s range, so a kind's nodes end
390+
// up contiguous. Slots start as `None` and are each filled exactly once
391+
// (the counts sum to the number of non-`Null` nodes).
392+
let mut nodes_by_kind = vec![None; node_count];
368393
let mut fill: Vec<u32> = kinds.iter().map(|k| k.start).collect();
369394
for (idx, node) in nodes.iter_enumerated() {
395+
// Unused indices from batch allocation stay `Null`; they carry no
396+
// encoded node and are never looked up by fingerprint, so skip them.
370397
if node.kind == DepKind::Null {
371398
continue;
372399
}
373400
let k = node.kind.as_usize();
374-
nodes_by_kind[fill[k] as usize] = idx;
401+
nodes_by_kind[fill[k] as usize] = Some(idx);
375402
fill[k] += 1;
376403
}
377-
let index = LazyNodeIndex { nodes_by_kind, kinds };
404+
let reverse_index = LazyNodeIndex { nodes_by_kind, kinds };
378405

379406
Arc::new(SerializedDepGraph {
380407
nodes,
381408
value_fingerprints,
382409
edge_list_indices,
383410
edge_list_data,
384-
index,
411+
reverse_index,
385412
session_count,
386413
})
387414
}

0 commit comments

Comments
 (0)