evobench_tools/evaluator/
index_by_call_path.rs

1//! Index spans at a call path from the top (excluding thread id,
2//! i.e. across all threads and processes)
3
4use std::{
5    collections::{HashMap, hash_map},
6    time::SystemTime,
7};
8
9use chj_rustbin::chunks::ChunksOp;
10use rayon::iter::{ParallelBridge, ParallelIterator};
11
12use crate::evaluator::data::log_data_tree::{LogDataTree, PathStringOptions, SpanId};
13
14#[derive(Debug, Default)]
15pub struct IndexByCallPath<'t> {
16    /// Spans at a call path from the top (excluding thread id,
17    /// i.e. across all threads and processes)
18    spans_by_call_path: HashMap<String, Vec<SpanId<'t>>>,
19}
20
21impl<'t> IndexByCallPath<'t> {
22    pub fn from_logdataindex(
23        log_data_tree: &LogDataTree<'t>,
24        path_string_optss: &[PathStringOptions],
25    ) -> Self {
26        let t0 = SystemTime::now();
27
28        let make_locals = || -> (String, String, HashMap<String, Vec<SpanId<'t>>>) {
29            (Default::default(), Default::default(), Default::default())
30        };
31        let spans_by_call_path = ChunksOp::chunks(log_data_tree.span_ids(), 1000)
32            .par_bridge()
33            .map(|span_ids| {
34                let (mut out_prefix, mut out_main, mut spans_by_call_path) = make_locals();
35                for span_id in span_ids {
36                    let span = span_id.get_from_db(log_data_tree);
37                    for opts in path_string_optss {
38                        let path = {
39                            // Calculate the path efficiently by reusing
40                            // buffers
41                            out_prefix.clear();
42                            out_main.clear();
43                            span.path_string(&opts, log_data_tree, &mut out_prefix, &mut out_main);
44                            out_prefix.push_str(&out_main);
45                            &*out_prefix
46                        };
47                        // Add span_id for the path
48                        match spans_by_call_path.get_mut(path) {
49                            Some(vec) => {
50                                vec.push(span_id);
51                            }
52                            None => {
53                                spans_by_call_path.insert(path.to_owned(), vec![span_id]);
54                            }
55                        }
56                    }
57                }
58                spans_by_call_path
59            })
60            .reduce(
61                || HashMap::new(),
62                |mut spans_by_call_path, hm| {
63                    for (k, mut v) in hm {
64                        match spans_by_call_path.entry(k) {
65                            hash_map::Entry::Occupied(occupied_entry) => {
66                                occupied_entry.into_mut().append(&mut v);
67                            }
68                            hash_map::Entry::Vacant(vacant_entry) => {
69                                vacant_entry.insert(v);
70                            }
71                        }
72                    }
73                    spans_by_call_path
74                },
75            );
76
77        let t1 = SystemTime::now();
78        eprintln!(
79            "t IndexByCallPath::from_logdataindex: {} s",
80            t1.duration_since(t0).unwrap().as_secs_f64()
81        );
82
83        Self { spans_by_call_path }
84    }
85
86    pub fn call_paths(&self) -> Vec<&String> {
87        let mut paths: Vec<&String> = self.spans_by_call_path.keys().collect();
88        paths.sort();
89        paths
90    }
91
92    pub fn spans_by_call_path(&self, call_path: &str) -> Option<&Vec<SpanId<'_>>> {
93        self.spans_by_call_path.get(call_path)
94    }
95}