mesa/src/compiler/rust/depth_first_search.rs
Faith Ekstrand f91f86dded compiler/rust: Implement dfs() non-recursively
Recursion in Rust can be annoyingly expensive.  The recursive DFS
implementation we have now is probably not terrible but these things
bloat in debug builds more than you'd expect.  This replaceas it with a
non-recursive implementation which uses a Vec instead.

Reviewed-by: Mel Henning <mhenning@darkrefraction.com>
Reviewed-by: Lorenzo Rossi <git@rossilorenzo.dev>
Reviewed-by: Mary Guillemard <mary@mary.zone>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37536>
2025-10-14 22:47:40 +00:00

78 lines
2.1 KiB
Rust

// Copyright © 2025 Collabora, Ltd.
// SPDX-License-Identifier: MIT
use crate::bitset::BitSet;
/// A trait implementing a depth-first search over a graph
pub trait DepthFirstSearch {
type ChildIter;
/// Pre-visit a node. This is called before any children or edges are
/// visited. Returns an iterator to this node's children in the graph.
fn pre(&mut self, id: usize) -> Self::ChildIter;
/// Visit an edge. An edge is visited before the child at the end of that
/// edge is pre-visited. Every edge is visited, even if if the node this
/// edge points to has already been visited.
fn edge(&mut self, _parent: usize, _child: usize) {
// Does nothing by default
}
/// Post-visit a node. This is called after all the children have been
/// visited.
fn post(&mut self, _id: usize) {
// Does nothing by default
}
}
struct DFSEntry<I: Iterator<Item = usize>> {
id: usize,
children: I,
}
pub fn dfs<I, D>(dfs: &mut D, start: usize)
where
I: Iterator<Item = usize>,
D: DepthFirstSearch<ChildIter = I>,
{
let mut seen = BitSet::new();
let mut stack = Vec::new();
seen.insert(start);
let children = dfs.pre(start);
stack.push(DFSEntry {
id: start,
children,
});
loop {
let Some(entry) = stack.last_mut() else {
break;
};
loop {
if let Some(id) = entry.children.next() {
dfs.edge(entry.id, id);
if seen.contains(id) {
continue;
}
seen.insert(id);
let children = dfs.pre(id);
stack.push(DFSEntry { id, children });
} else {
dfs.post(entry.id);
stack.pop();
}
// We're only looping here because we want to make the
// seen.contains() case a fast path. Both the case where we
// push the stack and recurse into a child or when we pop the
// stack want to continue in the next iteration of the outer
// loop.
break;
}
}
}