Skip to content

Commit 476e75d

Browse files
committed
Move a Node's parent into the descendents list.
`Node` has an optional parent and a list of other descendents. Most of the time the parent is treated the same as the other descendents -- error-handling is the exception -- and chaining them together for iteration has a non-trivial cost. This commit changes the representation. There is now a single list of descendants, and a boolean flag that indicates if there is a parent (in which case it is first descendent). This representation encodes the same information, in a way that is less idiomatic but cheaper to iterate over for the common case where the parent doesn't need special treatment. As a result, some benchmark workloads are up to 2% faster.
1 parent 5670d04 commit 476e75d

File tree

2 files changed

+36
-43
lines changed

2 files changed

+36
-43
lines changed

src/librustc_data_structures/obligation_forest/graphviz.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ impl<'a, O: ForestObligation + 'a> dot::GraphWalk<'a> for &'a ObligationForest<O
7474
.flat_map(|i| {
7575
let node = &self.nodes[i];
7676

77-
node.parent.iter()
78-
.chain(node.dependents.iter())
79-
.map(move |p| (p.index(), i))
77+
node.dependents.iter().map(move |p| (p.index(), i))
8078
})
8179
.collect()
8280
}

src/librustc_data_structures/obligation_forest/mod.rs

+35-40
Original file line numberDiff line numberDiff line change
@@ -177,21 +177,17 @@ struct Node<O> {
177177
obligation: O,
178178
state: Cell<NodeState>,
179179

180-
/// The parent of a node - the original obligation of which it is a
181-
/// subobligation. Except for error reporting, it is just like any member
182-
/// of `dependents`.
183-
///
184-
/// Unlike `ObligationForest::nodes`, this uses `NodeIndex` rather than
185-
/// `usize` for the index, because keeping the size down is more important
186-
/// than the cost of converting to a `usize` for indexing.
187-
parent: Option<NodeIndex>,
188-
189180
/// Obligations that depend on this obligation for their completion. They
190181
/// must all be in a non-pending state.
191-
///
192-
/// This uses `NodeIndex` for the same reason as `parent`.
193182
dependents: Vec<NodeIndex>,
194183

184+
/// If true, dependents[0] points to a "parent" node, which requires
185+
/// special treatment upon error but is otherwise treated the same.
186+
/// (It would be more idiomatic to store the parent node in a separate
187+
/// `Option<NodeIndex>` field, but that slows down the common case of
188+
/// iterating over the parent and other descendants together.)
189+
has_parent: bool,
190+
195191
/// Identifier of the obligation tree to which this node belongs.
196192
obligation_tree_id: ObligationTreeId,
197193
}
@@ -205,8 +201,13 @@ impl<O> Node<O> {
205201
Node {
206202
obligation,
207203
state: Cell::new(NodeState::Pending),
208-
parent,
209-
dependents: vec![],
204+
dependents:
205+
if let Some(parent_index) = parent {
206+
vec![parent_index]
207+
} else {
208+
vec![]
209+
},
210+
has_parent: parent.is_some(),
210211
obligation_tree_id,
211212
}
212213
}
@@ -315,13 +316,11 @@ impl<O: ForestObligation> ObligationForest<O> {
315316
obligation, parent, o.get());
316317
let node = &mut self.nodes[o.get().index()];
317318
if let Some(parent_index) = parent {
318-
// If the node is already in `waiting_cache`, it's already
319-
// been marked with a parent. (It's possible that parent
320-
// has been cleared by `apply_rewrites`, though.) So just
321-
// dump `parent` into `node.dependents`... unless it's
322-
// already in `node.dependents` or `node.parent`.
323-
if !node.dependents.contains(&parent_index) &&
324-
Some(parent_index) != node.parent {
319+
// If the node is already in `waiting_cache`, it has
320+
// already had its chance to be marked with a parent. So if
321+
// it's not already present, just dump `parent` into the
322+
// dependents as a non-parent.
323+
if !node.dependents.contains(&parent_index) {
325324
node.dependents.push(parent_index);
326325
}
327326
}
@@ -523,7 +522,7 @@ impl<O: ForestObligation> ObligationForest<O> {
523522
NodeState::Success => {
524523
node.state.set(NodeState::OnDfsStack);
525524
stack.push(i);
526-
for index in node.parent.iter().chain(node.dependents.iter()) {
525+
for index in node.dependents.iter() {
527526
self.find_cycles_from_node(stack, processor, index.index());
528527
}
529528
stack.pop();
@@ -549,12 +548,15 @@ impl<O: ForestObligation> ObligationForest<O> {
549548
let node = &self.nodes[i];
550549
node.state.set(NodeState::Error);
551550
trace.push(node.obligation.clone());
552-
error_stack.extend(node.dependents.iter().map(|index| index.index()));
553-
554-
// Loop to the parent.
555-
match node.parent {
556-
Some(parent_index) => i = parent_index.index(),
557-
None => break
551+
if node.has_parent {
552+
// The first dependent is the parent, which is treated
553+
// specially.
554+
error_stack.extend(node.dependents.iter().skip(1).map(|index| index.index()));
555+
i = node.dependents[0].index();
556+
} else {
557+
// No parent; treat all dependents non-specially.
558+
error_stack.extend(node.dependents.iter().map(|index| index.index()));
559+
break;
558560
}
559561
}
560562

@@ -565,9 +567,7 @@ impl<O: ForestObligation> ObligationForest<O> {
565567
_ => node.state.set(NodeState::Error),
566568
}
567569

568-
error_stack.extend(
569-
node.parent.iter().chain(node.dependents.iter()).map(|index| index.index())
570-
);
570+
error_stack.extend(node.dependents.iter().map(|index| index.index()));
571571
}
572572

573573
self.scratch.replace(error_stack);
@@ -577,7 +577,7 @@ impl<O: ForestObligation> ObligationForest<O> {
577577
// This always-inlined function is for the hot call site.
578578
#[inline(always)]
579579
fn inlined_mark_neighbors_as_waiting_from(&self, node: &Node<O>) {
580-
for dependent in node.parent.iter().chain(node.dependents.iter()) {
580+
for dependent in node.dependents.iter() {
581581
self.mark_as_waiting_from(&self.nodes[dependent.index()]);
582582
}
583583
}
@@ -706,20 +706,15 @@ impl<O: ForestObligation> ObligationForest<O> {
706706
let nodes_len = node_rewrites.len();
707707

708708
for node in &mut self.nodes {
709-
if let Some(index) = node.parent {
710-
let new_i = node_rewrites[index.index()];
711-
if new_i >= nodes_len {
712-
node.parent = None;
713-
} else {
714-
node.parent = Some(NodeIndex::new(new_i));
715-
}
716-
}
717-
718709
let mut i = 0;
719710
while i < node.dependents.len() {
720711
let new_i = node_rewrites[node.dependents[i].index()];
721712
if new_i >= nodes_len {
722713
node.dependents.swap_remove(i);
714+
if i == 0 && node.has_parent {
715+
// We just removed the parent.
716+
node.has_parent = false;
717+
}
723718
} else {
724719
node.dependents[i] = NodeIndex::new(new_i);
725720
i += 1;

0 commit comments

Comments
 (0)