diff options
| author | Nicholas Nethercote <nnethercote@mozilla.com> | 2019-09-17 12:08:24 +1000 |
|---|---|---|
| committer | Nicholas Nethercote <nnethercote@mozilla.com> | 2019-09-17 15:32:29 +1000 |
| commit | 476e75ded71ad6f573daabe2ca7a1be9fe3ce3cb (patch) | |
| tree | 94837a6293297f33ef727e4bd263bc4407504aca /src | |
| parent | 5670d048c0f88af9976b5505c7853b23dd06770d (diff) | |
| download | rust-476e75ded71ad6f573daabe2ca7a1be9fe3ce3cb.tar.gz rust-476e75ded71ad6f573daabe2ca7a1be9fe3ce3cb.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_data_structures/obligation_forest/graphviz.rs | 4 | ||||
| -rw-r--r-- | src/librustc_data_structures/obligation_forest/mod.rs | 75 |
2 files changed, 36 insertions, 43 deletions
diff --git a/src/librustc_data_structures/obligation_forest/graphviz.rs b/src/librustc_data_structures/obligation_forest/graphviz.rs index b2120b182fa..fe0a3cb0500 100644 --- a/src/librustc_data_structures/obligation_forest/graphviz.rs +++ b/src/librustc_data_structures/obligation_forest/graphviz.rs @@ -74,9 +74,7 @@ impl<'a, O: ForestObligation + 'a> dot::GraphWalk<'a> for &'a ObligationForest<O .flat_map(|i| { let node = &self.nodes[i]; - node.parent.iter() - .chain(node.dependents.iter()) - .map(move |p| (p.index(), i)) + node.dependents.iter().map(move |p| (p.index(), i)) }) .collect() } diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 189506bf8ab..0ae4a04eb50 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -177,21 +177,17 @@ struct Node<O> { obligation: O, state: Cell<NodeState>, - /// The parent of a node - the original obligation of which it is a - /// subobligation. Except for error reporting, it is just like any member - /// of `dependents`. - /// - /// Unlike `ObligationForest::nodes`, this uses `NodeIndex` rather than - /// `usize` for the index, because keeping the size down is more important - /// than the cost of converting to a `usize` for indexing. - parent: Option<NodeIndex>, - /// Obligations that depend on this obligation for their completion. They /// must all be in a non-pending state. - /// - /// This uses `NodeIndex` for the same reason as `parent`. dependents: Vec<NodeIndex>, + /// If true, dependents[0] points to a "parent" node, which requires + /// special treatment upon error but is otherwise treated the same. + /// (It would be more idiomatic to store the parent node in a separate + /// `Option<NodeIndex>` field, but that slows down the common case of + /// iterating over the parent and other descendants together.) + has_parent: bool, + /// Identifier of the obligation tree to which this node belongs. obligation_tree_id: ObligationTreeId, } @@ -205,8 +201,13 @@ impl<O> Node<O> { Node { obligation, state: Cell::new(NodeState::Pending), - parent, - dependents: vec![], + dependents: + if let Some(parent_index) = parent { + vec![parent_index] + } else { + vec![] + }, + has_parent: parent.is_some(), obligation_tree_id, } } @@ -315,13 +316,11 @@ impl<O: ForestObligation> ObligationForest<O> { obligation, parent, o.get()); let node = &mut self.nodes[o.get().index()]; if let Some(parent_index) = parent { - // If the node is already in `waiting_cache`, it's already - // been marked with a parent. (It's possible that parent - // has been cleared by `apply_rewrites`, though.) So just - // dump `parent` into `node.dependents`... unless it's - // already in `node.dependents` or `node.parent`. - if !node.dependents.contains(&parent_index) && - Some(parent_index) != node.parent { + // If the node is already in `waiting_cache`, it has + // already had its chance to be marked with a parent. So if + // it's not already present, just dump `parent` into the + // dependents as a non-parent. + if !node.dependents.contains(&parent_index) { node.dependents.push(parent_index); } } @@ -523,7 +522,7 @@ impl<O: ForestObligation> ObligationForest<O> { NodeState::Success => { node.state.set(NodeState::OnDfsStack); stack.push(i); - for index in node.parent.iter().chain(node.dependents.iter()) { + for index in node.dependents.iter() { self.find_cycles_from_node(stack, processor, index.index()); } stack.pop(); @@ -549,12 +548,15 @@ impl<O: ForestObligation> ObligationForest<O> { let node = &self.nodes[i]; node.state.set(NodeState::Error); trace.push(node.obligation.clone()); - error_stack.extend(node.dependents.iter().map(|index| index.index())); - - // Loop to the parent. - match node.parent { - Some(parent_index) => i = parent_index.index(), - None => break + if node.has_parent { + // The first dependent is the parent, which is treated + // specially. + error_stack.extend(node.dependents.iter().skip(1).map(|index| index.index())); + i = node.dependents[0].index(); + } else { + // No parent; treat all dependents non-specially. + error_stack.extend(node.dependents.iter().map(|index| index.index())); + break; } } @@ -565,9 +567,7 @@ impl<O: ForestObligation> ObligationForest<O> { _ => node.state.set(NodeState::Error), } - error_stack.extend( - node.parent.iter().chain(node.dependents.iter()).map(|index| index.index()) - ); + error_stack.extend(node.dependents.iter().map(|index| index.index())); } self.scratch.replace(error_stack); @@ -577,7 +577,7 @@ impl<O: ForestObligation> ObligationForest<O> { // This always-inlined function is for the hot call site. #[inline(always)] fn inlined_mark_neighbors_as_waiting_from(&self, node: &Node<O>) { - for dependent in node.parent.iter().chain(node.dependents.iter()) { + for dependent in node.dependents.iter() { self.mark_as_waiting_from(&self.nodes[dependent.index()]); } } @@ -706,20 +706,15 @@ impl<O: ForestObligation> ObligationForest<O> { let nodes_len = node_rewrites.len(); for node in &mut self.nodes { - if let Some(index) = node.parent { - let new_i = node_rewrites[index.index()]; - if new_i >= nodes_len { - node.parent = None; - } else { - node.parent = Some(NodeIndex::new(new_i)); - } - } - let mut i = 0; while i < node.dependents.len() { let new_i = node_rewrites[node.dependents[i].index()]; if new_i >= nodes_len { node.dependents.swap_remove(i); + if i == 0 && node.has_parent { + // We just removed the parent. + node.has_parent = false; + } } else { node.dependents[i] = NodeIndex::new(new_i); i += 1; |
