Skip to content

Commit 04aebfc

Browse files
Serialized instead of parallel path finding
1 parent cd58abb commit 04aebfc

File tree

4 files changed

+188
-121
lines changed

4 files changed

+188
-121
lines changed

stack-graphs/src/arena.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ impl<T> Arena<T> {
187187
#[inline(always)]
188188
pub(crate) fn clear(&mut self) {
189189
self.items.clear();
190+
self.items.push(MaybeUninit::uninit());
190191
}
191192

192193
/// Adds a new instance to this arena, returning a stable handle to it.
@@ -294,6 +295,7 @@ impl<H, T> SupplementalArena<H, T> {
294295
#[inline(always)]
295296
pub(crate) fn clear(&mut self) {
296297
self.items.clear();
298+
self.items.push(MaybeUninit::uninit());
297299
}
298300

299301
/// Creates a new, empty supplemental arena, preallocating enough space to store supplemental

stack-graphs/src/cycles.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ pub struct SimilarPathDetector<P> {
5252
paths: HashMap<PathKey, SmallVec<[P; 4]>>,
5353
}
5454

55+
impl<P> SimilarPathDetector<P> {
56+
pub fn clear(&mut self) {
57+
self.paths.clear();
58+
}
59+
}
60+
5561
#[doc(hidden)]
5662
#[derive(Clone, Eq, Hash, PartialEq)]
5763
pub struct PathKey {
@@ -163,6 +169,11 @@ impl<H> Appendables<H> {
163169
interned: Arena::new(),
164170
}
165171
}
172+
173+
pub fn clear(&mut self) {
174+
self.elements.clear();
175+
self.interned.clear();
176+
}
166177
}
167178

168179
/// Enum that unifies handles to initial paths interned in the cycle detector, and appended

stack-graphs/src/stitching.rs

Lines changed: 126 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ use std::collections::VecDeque;
4141
#[cfg(feature = "copious-debugging")]
4242
use std::fmt::Display;
4343

44-
use itertools::izip;
45-
use itertools::Itertools;
46-
4744
use crate::arena::Arena;
4845
use crate::arena::Handle;
4946
use crate::arena::HandleSet;
@@ -763,17 +760,13 @@ impl<'a> Display for DisplaySymbolStackKey<'a> {
763760
pub struct ForwardPartialPathStitcher<H> {
764761
candidates: Vec<H>,
765762
extensions: Vec<(PartialPath, AppendingCycleDetector<H>)>,
766-
queue: VecDeque<(PartialPath, AppendingCycleDetector<H>, bool)>,
763+
queue: VecDeque<(PartialPath, PathStitchingState<H>)>,
767764
// tracks the number of initial paths in the queue because we do not want call
768765
// extend_until on those
769766
initial_paths: usize,
770767
// next_iteration is a tuple of queues instead of an queue of tuples so that the path queue
771768
// can be cheaply exposed through the C API as a continuous memory block
772-
next_iteration: (
773-
VecDeque<PartialPath>,
774-
VecDeque<AppendingCycleDetector<H>>,
775-
VecDeque<bool>,
776-
),
769+
next_iteration: (VecDeque<PartialPath>, VecDeque<PathStitchingState<H>>),
777770
appended_paths: Appendables<H>,
778771
similar_path_detector: Option<SimilarPathDetector<PartialPath>>,
779772
check_only_join_nodes: bool,
@@ -782,40 +775,83 @@ pub struct ForwardPartialPathStitcher<H> {
782775
phase_number: usize,
783776
}
784777

778+
struct PathStitchingState<H> {
779+
cycle_detector: AppendingCycleDetector<H>,
780+
has_split: bool,
781+
}
782+
783+
impl<H> Default for ForwardPartialPathStitcher<H> {
784+
fn default() -> Self {
785+
Self {
786+
candidates: Vec::new(),
787+
extensions: Vec::new(),
788+
queue: VecDeque::new(),
789+
initial_paths: 0,
790+
next_iteration: (VecDeque::new(), VecDeque::new()),
791+
appended_paths: Appendables::new(),
792+
similar_path_detector: Some(SimilarPathDetector::new()),
793+
// By default, all nodes are checked for cycles and (if enabled) similarity
794+
check_only_join_nodes: false,
795+
// By default, there's no artificial bound on the amount of work done per phase
796+
max_work_per_phase: usize::MAX,
797+
#[cfg(feature = "copious-debugging")]
798+
phase_number: 1,
799+
}
800+
}
801+
}
802+
785803
impl<H> ForwardPartialPathStitcher<H> {
786804
/// Creates a new forward partial path stitcher that is "seeded" with a set of initial partial
787805
/// paths. If the sticher is used to find complete paths, it is the responsibility of the caller
788806
/// to ensure precondition variables are eliminated by calling [`PartialPath::eliminate_precondition_stack_variables`][].
789807
pub fn from_partial_paths<I>(
790-
_graph: &StackGraph,
791-
_partials: &mut PartialPaths,
808+
graph: &StackGraph,
809+
partials: &mut PartialPaths,
792810
initial_partial_paths: I,
793811
) -> Self
794812
where
795813
I: IntoIterator<Item = PartialPath>,
796814
{
797-
let mut appended_paths = Appendables::new();
798-
let next_iteration: (VecDeque<_>, VecDeque<_>, VecDeque<_>) = initial_partial_paths
815+
let mut result = Self::default();
816+
result.reset(graph, partials, initial_partial_paths);
817+
result
818+
}
819+
820+
pub fn reset<I>(
821+
&mut self,
822+
_graph: &StackGraph,
823+
_partials: &mut PartialPaths,
824+
initial_partial_paths: I,
825+
) where
826+
I: IntoIterator<Item = PartialPath>,
827+
{
828+
self.appended_paths.clear();
829+
self.next_iteration = initial_partial_paths
799830
.into_iter()
800831
.map(|p| {
801-
let c = AppendingCycleDetector::from(&mut appended_paths, p.clone().into());
802-
(p, c, false)
832+
let cycle_detector =
833+
AppendingCycleDetector::from(&mut self.appended_paths, p.clone().into());
834+
(
835+
p,
836+
PathStitchingState {
837+
cycle_detector,
838+
has_split: false,
839+
},
840+
)
803841
})
804-
.multiunzip();
805-
Self {
806-
candidates: Vec::new(),
807-
extensions: Vec::new(),
808-
queue: VecDeque::new(),
809-
initial_paths: next_iteration.0.len(),
810-
next_iteration,
811-
appended_paths,
812-
similar_path_detector: Some(SimilarPathDetector::new()),
813-
// By default, all nodes are checked for cycles and (if enabled) similarity
814-
check_only_join_nodes: false,
815-
// By default, there's no artificial bound on the amount of work done per phase
816-
max_work_per_phase: usize::MAX,
817-
#[cfg(feature = "copious-debugging")]
818-
phase_number: 1,
842+
.unzip();
843+
self.initial_paths = self.next_iteration.0.len();
844+
self.candidates.clear();
845+
self.extensions.clear();
846+
self.queue.clear();
847+
if let Some(similar_path_detector) = &mut self.similar_path_detector {
848+
similar_path_detector.clear();
849+
}
850+
// keep self.check_only_join_nodes
851+
// keep self.max_work_per_phase
852+
#[cfg(feature = "copious-debugging")]
853+
{
854+
self.phase_number = 1;
819855
}
820856
}
821857
}
@@ -952,7 +988,6 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
952988
let new_has_split = has_split || self.extensions.len() > 1;
953989
self.next_iteration.0.reserve(extension_count);
954990
self.next_iteration.1.reserve(extension_count);
955-
self.next_iteration.2.reserve(extension_count);
956991
for (new_partial_path, new_cycle_detector) in self.extensions.drain(..) {
957992
let check_similar_path = new_has_split
958993
&& (!self.check_only_join_nodes
@@ -990,8 +1025,10 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
9901025
}
9911026

9921027
self.next_iteration.0.push(new_partial_path);
993-
self.next_iteration.1.push(new_cycle_detector);
994-
self.next_iteration.2.push(new_has_split);
1028+
self.next_iteration.1.push(PathStitchingState {
1029+
cycle_detector: new_cycle_detector,
1030+
has_split: new_has_split,
1031+
});
9951032
}
9961033

9971034
candidate_count
@@ -1022,13 +1059,21 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
10221059
E: Fn(&StackGraph, &mut PartialPaths, &PartialPath) -> bool,
10231060
{
10241061
copious_debugging!("==> Start phase {}", self.phase_number);
1025-
self.queue.extend(izip!(
1026-
self.next_iteration.0.drain(..),
1027-
self.next_iteration.1.drain(..),
1028-
self.next_iteration.2.drain(..),
1029-
));
1062+
self.queue.extend(
1063+
self.next_iteration
1064+
.0
1065+
.drain(..)
1066+
.zip(self.next_iteration.1.drain(..)),
1067+
);
10301068
let mut work_performed = 0;
1031-
while let Some((partial_path, cycle_detector, has_split)) = self.queue.pop_front() {
1069+
while let Some((
1070+
partial_path,
1071+
PathStitchingState {
1072+
cycle_detector,
1073+
has_split,
1074+
},
1075+
)) = self.queue.pop_front()
1076+
{
10321077
let (graph, partials, _) = candidates.get_graph_partials_and_db();
10331078
copious_debugging!(
10341079
"--> Candidate partial path {}",
@@ -1101,18 +1146,22 @@ impl ForwardPartialPathStitcher<Edge> {
11011146
.filter(|node| graph[*node].is_endpoint())
11021147
.map(|node| PartialPath::from_node(graph, partials, node))
11031148
.collect::<Vec<_>>();
1104-
let mut stitcher =
1105-
ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths);
1149+
1150+
let mut stitcher = ForwardPartialPathStitcher::default();
11061151
stitcher.set_check_only_join_nodes(true);
1107-
while !stitcher.is_complete() {
1108-
cancellation_flag.check("finding complete partial paths")?;
1109-
stitcher.process_next_phase(
1110-
&mut GraphEdgeCandidates::new(graph, partials, Some(file)),
1111-
|g, _ps, p| !as_complete_as_necessary(g, p),
1112-
);
1113-
for path in stitcher.previous_phase_partial_paths() {
1114-
if as_complete_as_necessary(graph, path) {
1115-
visit(graph, partials, path);
1152+
1153+
for initial_path in initial_paths {
1154+
stitcher.reset(graph, partials, std::iter::once(initial_path));
1155+
while !stitcher.is_complete() {
1156+
cancellation_flag.check("finding complete partial paths")?;
1157+
stitcher.process_next_phase(
1158+
&mut GraphEdgeCandidates::new(graph, partials, Some(file)),
1159+
|g, _ps, p| !as_complete_as_necessary(g, p),
1160+
);
1161+
for path in stitcher.previous_phase_partial_paths() {
1162+
if as_complete_as_necessary(graph, path) {
1163+
visit(graph, partials, path);
1164+
}
11161165
}
11171166
}
11181167
}
@@ -1146,33 +1195,38 @@ impl<H: Clone> ForwardPartialPathStitcher<H> {
11461195
F: FnMut(&StackGraph, &mut PartialPaths, &PartialPath),
11471196
Err: std::convert::From<CancellationError>,
11481197
{
1149-
let mut stitcher = {
1150-
let (graph, partials, _) = candidates.get_graph_partials_and_db();
1151-
let initial_paths = starting_nodes
1152-
.into_iter()
1153-
.filter(|n| graph[*n].is_reference())
1154-
.map(|n| {
1155-
let mut p = PartialPath::from_node(graph, partials, n);
1156-
p.eliminate_precondition_stack_variables(partials);
1157-
p
1158-
})
1159-
.collect::<Vec<_>>();
1160-
ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths)
1161-
};
1198+
let (graph, partials, _) = candidates.get_graph_partials_and_db();
1199+
let initial_paths = starting_nodes
1200+
.into_iter()
1201+
.filter(|n| graph[*n].is_reference())
1202+
.map(|n| {
1203+
let mut p = PartialPath::from_node(graph, partials, n);
1204+
p.eliminate_precondition_stack_variables(partials);
1205+
p
1206+
})
1207+
.collect::<Vec<_>>();
1208+
1209+
let mut stitcher = ForwardPartialPathStitcher::default();
11621210
stitcher.set_check_only_join_nodes(true);
1163-
while !stitcher.is_complete() {
1164-
cancellation_flag.check("finding complete partial paths")?;
1165-
for path in stitcher.previous_phase_partial_paths() {
1166-
candidates.load_forward_candidates(path, cancellation_flag)?;
1167-
}
1168-
stitcher.process_next_phase(candidates, |_, _, _| true);
1211+
1212+
for initial_path in initial_paths {
11691213
let (graph, partials, _) = candidates.get_graph_partials_and_db();
1170-
for path in stitcher.previous_phase_partial_paths() {
1171-
if path.is_complete(graph) {
1172-
visit(graph, partials, path);
1214+
stitcher.reset(graph, partials, std::iter::once(initial_path));
1215+
while !stitcher.is_complete() {
1216+
cancellation_flag.check("finding complete partial paths")?;
1217+
for path in stitcher.previous_phase_partial_paths() {
1218+
candidates.load_forward_candidates(path, cancellation_flag)?;
1219+
}
1220+
stitcher.process_next_phase(candidates, |_, _, _| true);
1221+
let (graph, partials, _) = candidates.get_graph_partials_and_db();
1222+
for path in stitcher.previous_phase_partial_paths() {
1223+
if path.is_complete(graph) {
1224+
visit(graph, partials, path);
1225+
}
11731226
}
11741227
}
11751228
}
1229+
11761230
Ok(())
11771231
}
11781232
}

0 commit comments

Comments
 (0)