diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index e989dc471..0f00ad16f 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -50,12 +50,8 @@ use crate::stitching::ToAppendable; /// Helps detect similar paths in the path-finding algorithm. pub struct SimilarPathDetector

{ - paths: HashMap; 4]>>, -} - -struct SimilarPath

{ - path: P, - count: usize, + paths: HashMap>, + counts: Option>>, } #[doc(hidden)] @@ -98,13 +94,23 @@ where pub fn new() -> SimilarPathDetector

{ SimilarPathDetector { paths: HashMap::new(), + counts: None, + } + } + + /// Set whether to collect statistics for this similar path detector. + pub fn set_collect_stats(&mut self, collect_stats: bool) { + if !collect_stats { + self.counts = None; + } else if self.counts.is_none() { + self.counts = Some(HashMap::new()); } } - /// Determines whether we should process this path during the path-finding algorithm. If we have seen - /// a path with the same start and end node, and the same pre- and postcondition, then we return false. - /// Otherwise, we return true. - pub fn has_similar_path( + /// Add a apth, and determine whether we should process this path during the path-finding algorithm. + /// If we have seen a path with the same start and end node, and the same pre- and postcondition, then + /// we return false. Otherwise, we return true. + pub fn add_path( &mut self, _graph: &StackGraph, arena: &mut P::Arena, @@ -119,22 +125,31 @@ where // Iterate through the bucket to determine if this paths is better than any already known // path. Note that the bucket might be modified during the loop if a path is removed which // is shadowed by the new path! - let possibly_similar_paths = self.paths.entry(key).or_default(); + let possibly_similar_paths = self.paths.entry(key.clone()).or_default(); + let mut possible_similar_counts = self + .counts + .as_mut() + .map(move |cs| cs.entry(key).or_default()); let mut idx = 0; let mut count = 0; while idx < possibly_similar_paths.len() { let other_path = &mut possibly_similar_paths[idx]; - match cmp(arena, path, &other_path.path) { + match cmp(arena, path, other_path) { Some(Ordering::Less) => { // the new path is better, remove the old one - count += other_path.count; possibly_similar_paths.remove(idx); + if let Some(possible_similar_counts) = possible_similar_counts.as_mut() { + count += possible_similar_counts[idx]; + possible_similar_counts.remove(idx); + } // keep `idx` which now points to the next element continue; } Some(_) => { // the new path is equal or worse, and ignored - other_path.count += 1; + if let Some(possible_similar_counts) = possible_similar_counts { + possible_similar_counts[idx] += 1; + } return true; } None => { @@ -144,10 +159,10 @@ where } // this path is either new or better, keep it - possibly_similar_paths.push(SimilarPath { - path: path.clone(), - count, - }); + possibly_similar_paths.push(path.clone()); + if let Some(possible_similar_counts) = possible_similar_counts { + possible_similar_counts.push(count); + } false } @@ -159,10 +174,12 @@ where // Returns the distribution of similar path counts. pub fn stats(&self) -> SimilarPathStats { let mut stats = SimilarPathStats::default(); - for bucket in self.paths.values() { - stats.similar_path_bucket_size.record(bucket.len()); - for path in bucket.iter() { - stats.similar_path_count.record(path.count); + if let Some(counts) = &self.counts { + for bucket in counts.values() { + stats.similar_path_bucket_size.record(bucket.len()); + for count in bucket.iter() { + stats.similar_path_count.record(*count); + } } } stats diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 46881037d..99aaa8df1 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -896,7 +896,9 @@ impl ForwardPartialPathStitcher { if !detect_similar_paths { self.similar_path_detector = None; } else if self.similar_path_detector.is_none() { - self.similar_path_detector = Some(SimilarPathDetector::new()); + let mut similar_path_detector = SimilarPathDetector::new(); + similar_path_detector.set_collect_stats(self.stats.is_some()); + self.similar_path_detector = Some(similar_path_detector); } } @@ -926,6 +928,9 @@ impl ForwardPartialPathStitcher { stats.initial_paths.record(self.initial_paths); self.stats = Some(stats); } + if let Some(similar_path_detector) = &mut self.similar_path_detector { + similar_path_detector.set_collect_stats(collect_stats); + } } pub fn into_stats(mut self) -> Stats { @@ -1050,7 +1055,7 @@ impl ForwardPartialPathStitcher { let (graph, partials, _) = candidates.get_graph_partials_and_db(); if check_similar_path { if let Some(similar_path_detector) = &mut self.similar_path_detector { - if similar_path_detector.has_similar_path( + if similar_path_detector.add_path( graph, partials, &new_partial_path,