Fast path that skips over unchanged obligations in process_obligations
- only borrow the refcell once per loop - avoid complex matches to reduce branch paths in the hot loop - use a by-ref fast path that avoids mutations at the expense of having false negatives
This commit is contained in:
parent
03b01c5bec
commit
7cce618d18
6 changed files with 79 additions and 6 deletions
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
arrayvec = { version = "0.7", default-features = false }
|
||||
bitflags = "1.2.1"
|
||||
cfg-if = "1.0"
|
||||
ena = "0.14.1"
|
||||
ena = "0.14.2"
|
||||
indexmap = { version = "1.9.1" }
|
||||
jobserver_crate = { version = "0.1.13", package = "jobserver" }
|
||||
libc = "0.2"
|
||||
|
|
|
@ -97,7 +97,17 @@ pub trait ObligationProcessor {
|
|||
type Error: Debug;
|
||||
type OUT: OutcomeTrait<Obligation = Self::Obligation, Error = Error<Self::Obligation, Self::Error>>;
|
||||
|
||||
fn needs_process_obligation(&self, obligation: &Self::Obligation) -> bool;
|
||||
/// Implementations can provide a fast-path to obligation-processing
|
||||
/// by counting the prefix of the passed iterator for which
|
||||
/// `needs_process_obligation` would return false.
|
||||
fn skippable_obligations<'a>(
|
||||
&'a self,
|
||||
_it: impl Iterator<Item = &'a Self::Obligation>,
|
||||
) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn needs_process_obligation(&self, _obligation: &Self::Obligation) -> bool;
|
||||
|
||||
fn process_obligation(
|
||||
&mut self,
|
||||
|
@ -416,6 +426,10 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
loop {
|
||||
let mut has_changed = false;
|
||||
|
||||
// This is the super fast path for cheap-to-check conditions.
|
||||
let mut index =
|
||||
processor.skippable_obligations(self.nodes.iter().map(|n| &n.obligation));
|
||||
|
||||
// Note that the loop body can append new nodes, and those new nodes
|
||||
// will then be processed by subsequent iterations of the loop.
|
||||
//
|
||||
|
@ -424,9 +438,8 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
// `for index in 0..self.nodes.len() { ... }` because the range would
|
||||
// be computed with the initial length, and we would miss the appended
|
||||
// nodes. Therefore we use a `while` loop.
|
||||
let mut index = 0;
|
||||
while let Some(node) = self.nodes.get_mut(index) {
|
||||
// This test is extremely hot.
|
||||
// This is the moderately fast path when the prefix skipping above didn't work out.
|
||||
if node.state.get() != NodeState::Pending
|
||||
|| !processor.needs_process_obligation(&node.obligation)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue