Skip to content

Commit 7b33f7e

Browse files
committed
Optimize ObligationForest's NodeState handling.
This commit partially inlines two functions, `find_cycles_from_node` and `mark_as_waiting_from`, at two call sites in order to avoid function unnecessary function calls on hot paths. It also fully inlines and removes `is_popped`. These changes speeds up rustc-benchmarks/inflate-0.1.0 by about 2% when doing debug builds with a stage1 compiler.
1 parent 4497196 commit 7b33f7e

File tree

1 file changed

+37
-34
lines changed
  • src/librustc_data_structures/obligation_forest

1 file changed

+37
-34
lines changed

src/librustc_data_structures/obligation_forest/mod.rs

+37-34
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,16 @@ impl<O: ForestObligation> ObligationForest<O> {
377377
{
378378
let mut stack = self.scratch.take().unwrap();
379379

380-
for node in 0..self.nodes.len() {
381-
self.find_cycles_from_node(&mut stack, processor, node);
380+
for index in 0..self.nodes.len() {
381+
// For rustc-benchmarks/inflate-0.1.0 this state test is extremely
382+
// hot and the state is almost always `Pending` or `Waiting`. It's
383+
// a win to handle the no-op cases immediately to avoid the cost of
384+
// the function call.
385+
let state = self.nodes[index].state.get();
386+
match state {
387+
NodeState::Waiting | NodeState::Pending | NodeState::Done | NodeState::Error => {},
388+
_ => self.find_cycles_from_node(&mut stack, processor, index),
389+
}
382390
}
383391

384392
self.scratch = Some(stack);
@@ -476,7 +484,18 @@ impl<O: ForestObligation> ObligationForest<O> {
476484
trace
477485
}
478486

479-
/// Marks all nodes that depend on a pending node as NodeState;:Waiting.
487+
#[inline]
488+
fn mark_neighbors_as_waiting_from(&self, node: &Node<O>) {
489+
if let Some(parent) = node.parent {
490+
self.mark_as_waiting_from(&self.nodes[parent.get()]);
491+
}
492+
493+
for dependent in &node.dependents {
494+
self.mark_as_waiting_from(&self.nodes[dependent.get()]);
495+
}
496+
}
497+
498+
/// Marks all nodes that depend on a pending node as NodeState::Waiting.
480499
fn mark_as_waiting(&self) {
481500
for node in &self.nodes {
482501
if node.state.get() == NodeState::Waiting {
@@ -486,27 +505,19 @@ impl<O: ForestObligation> ObligationForest<O> {
486505

487506
for node in &self.nodes {
488507
if node.state.get() == NodeState::Pending {
489-
self.mark_as_waiting_from(node)
508+
self.mark_neighbors_as_waiting_from(node);
490509
}
491510
}
492511
}
493512

494513
fn mark_as_waiting_from(&self, node: &Node<O>) {
495514
match node.state.get() {
496-
NodeState::Pending | NodeState::Done => {},
497515
NodeState::Waiting | NodeState::Error | NodeState::OnDfsStack => return,
498-
NodeState::Success => {
499-
node.state.set(NodeState::Waiting);
500-
}
501-
}
502-
503-
if let Some(parent) = node.parent {
504-
self.mark_as_waiting_from(&self.nodes[parent.get()]);
516+
NodeState::Success => node.state.set(NodeState::Waiting),
517+
NodeState::Pending | NodeState::Done => {},
505518
}
506519

507-
for dependent in &node.dependents {
508-
self.mark_as_waiting_from(&self.nodes[dependent.get()]);
509-
}
520+
self.mark_neighbors_as_waiting_from(node);
510521
}
511522

512523
/// Compresses the vector, removing all popped nodes. This adjusts
@@ -532,28 +543,28 @@ impl<O: ForestObligation> ObligationForest<O> {
532543
// self.nodes[i..] are unchanged
533544
for i in 0..self.nodes.len() {
534545
match self.nodes[i].state.get() {
546+
NodeState::Pending | NodeState::Waiting => {
547+
if dead_nodes > 0 {
548+
self.nodes.swap(i, i - dead_nodes);
549+
node_rewrites[i] -= dead_nodes;
550+
}
551+
}
535552
NodeState::Done => {
536553
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
537554
// FIXME(HashMap): why can't I get my key back?
538555
self.done_cache.insert(self.nodes[i].obligation.as_predicate().clone());
556+
node_rewrites[i] = nodes_len;
557+
dead_nodes += 1;
539558
}
540559
NodeState::Error => {
541560
// We *intentionally* remove the node from the cache at this point. Otherwise
542561
// tests must come up with a different type on every type error they
543562
// check against.
544563
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
564+
node_rewrites[i] = nodes_len;
565+
dead_nodes += 1;
545566
}
546-
_ => {}
547-
}
548-
549-
if self.nodes[i].is_popped() {
550-
node_rewrites[i] = nodes_len;
551-
dead_nodes += 1;
552-
} else {
553-
if dead_nodes > 0 {
554-
self.nodes.swap(i, i - dead_nodes);
555-
node_rewrites[i] -= dead_nodes;
556-
}
567+
NodeState::OnDfsStack | NodeState::Success => unreachable!()
557568
}
558569
}
559570

@@ -633,12 +644,4 @@ impl<O> Node<O> {
633644
dependents: vec![],
634645
}
635646
}
636-
637-
fn is_popped(&self) -> bool {
638-
match self.state.get() {
639-
NodeState::Pending | NodeState::Waiting => false,
640-
NodeState::Error | NodeState::Done => true,
641-
NodeState::OnDfsStack | NodeState::Success => unreachable!()
642-
}
643-
}
644647
}

0 commit comments

Comments
 (0)