mc_core/
perf.rs

1//! A really unsafe module made for internal use in MinecraftRS and its examples.
2
3use std::time::{Duration, Instant};
4use std::rc::{Rc, Weak};
5use std::cell::RefCell;
6
7
8thread_local! {
9    static STACK: RefCell<Stack> = RefCell::new(Stack::new());
10}
11
12static mut ENABLED: bool = false;
13
14/*static STACK_LIST: Lazy<RwLock<StackList>> = Lazy::new(|| RwLock::new(StackList {
15    stacks: Vec::new()
16}));
17
18
19struct StackList {
20    stacks: Vec<(Thread, Rc<RefCell<Frame>>)>
21}*/
22
23
24struct Stack {
25    thread_name: String,
26    root_frame: Rc<RefCell<Frame>>,
27    current_frame: Rc<RefCell<Frame>>,
28}
29
30/*impl Drop for Stack {
31    fn drop(&mut self) {
32        let current_thread = std::thread::current();
33        STACK_LIST.write().unwrap().stacks.retain(move |(thread, _)| current_thread.id() == thread.id());
34    }
35}*/
36
37impl Stack {
38
39    fn new() -> Self {
40
41        let thread = std::thread::current();
42        let root_frame = Frame::new("root", Weak::new());
43
44        /*STACK_LIST.write().unwrap().stacks.push((
45            thread.clone(),
46            Rc::clone(&root_frame)
47        ));*/
48
49        Self {
50            thread_name: thread.name().unwrap_or("").to_string(),
51            root_frame: Rc::clone(&root_frame),
52            current_frame: root_frame
53        }
54
55    }
56
57    fn push(&mut self, name: &'static str) {
58        let frame = (*self.current_frame).borrow_mut().get_or_create_child(name, &self.current_frame);
59        (*frame).borrow_mut().start = Instant::now();
60        self.current_frame = frame;
61    }
62
63    fn pop(&mut self) {
64
65        let parent_frame = {
66            let mut current_frame = (*self.current_frame).borrow_mut();
67            let duration = current_frame.start.elapsed();
68            current_frame.duration_sum += duration;
69            current_frame.duration_count += 1;
70            Weak::upgrade(&current_frame.parent)
71        };
72
73        self.current_frame = parent_frame.expect("You can't pop the root frame.");
74
75    }
76
77    fn debug(&self) {
78        println!("----< thread: {} >----", self.thread_name);
79        Self::debug_frame(&self.root_frame, 0);
80    }
81
82    fn debug_frame(frame: &Rc<RefCell<Frame>>, tab: u16) {
83
84        let guard = (**frame).borrow();
85        for _ in 0..tab {
86            print!("  ");
87        }
88
89        if guard.duration_count > 0 {
90            println!("- {} (x{}, total: {:.2?}, avg: {:.2?})", guard.name, guard.duration_count, guard.duration_sum, guard.duration_sum / guard.duration_count);
91        } else {
92            println!("- {}", guard.name);
93        }
94
95        for child in &guard.children {
96            Self::debug_frame(&child.frame, tab + 1);
97        }
98
99    }
100
101}
102
103
104struct Frame {
105    /// An identifier name for the frame.
106    name: &'static str,
107    /// Children frames.
108    children: Vec<FrameChild>,
109    /// Parent frame, dangling in case of root frame.
110    parent: Weak<RefCell<Frame>>,
111    /// Last start instant.
112    start: Instant,
113    /// Total sum of all durations.
114    duration_sum: Duration,
115    /// The number of samples summed in the `duration_sum` field, used for average.
116    duration_count: u32
117}
118
119struct FrameChild {
120    name: &'static str,
121    frame: Rc<RefCell<Frame>>
122}
123
124impl Frame {
125
126    fn new(name: &'static str, parent: Weak<RefCell<Frame>>) -> Rc<RefCell<Self>> {
127        Rc::new(RefCell::new(Self {
128            name,
129            children: Vec::new(),
130            parent,
131            start: Instant::now(),
132            duration_sum: Duration::from_secs(0),
133            duration_count: 0
134        }))
135    }
136
137    fn push_child(&mut self, name: &'static str, parent: &Rc<RefCell<Frame>>) -> Rc<RefCell<Frame>> {
138        let child = Self::new(name, Rc::downgrade(parent));
139        self.children.push(FrameChild {
140            name,
141            frame: Rc::clone(&child)
142        });
143        child
144    }
145
146    fn get_or_create_child(&mut self, name: &'static str, parent: &Rc<RefCell<Frame>>) -> Rc<RefCell<Frame>> {
147
148        // The algorithm is spacial here because we want to optimize in case of loop on
149        // the same child frame. Or if the whole section loop over to the first child.
150
151        let len = self.children.len();
152
153        let end = if len == 0 {
154            return self.push_child(name, parent);
155        } else if len > 1 {
156            let last_child = &self.children[len - 1];
157            if last_child.name == name {
158                return Rc::clone(&last_child.frame);
159            }
160            len - 1
161        } else {
162            len
163        };
164
165        // Since the last child has already been checked, ignore it.
166        let first_child = self.children[..end].iter()
167            .find(move |frame| frame.name == name);
168
169        if let Some(child) = first_child {
170            Rc::clone(&child.frame)
171        } else {
172            self.push_child(name, parent)
173        }
174
175    }
176
177}
178
179
180pub fn push(name: &'static str) {
181    if is_enabled() {
182        STACK.with(move |stack| stack.borrow_mut().push(name));
183    }
184}
185
186pub fn pop() {
187    if is_enabled() {
188        STACK.with(move |stack| stack.borrow_mut().pop());
189    }
190}
191
192pub fn pop_push(name: &'static str) {
193    if is_enabled() {
194        STACK.with(move |stack| {
195            let mut guard = stack.borrow_mut();
196            guard.pop();
197            guard.push(name);
198        });
199    }
200}
201
202pub fn frame<F: FnOnce()>(name: &'static str, func: F) {
203    if is_enabled() {
204        STACK.with(move |stack| {
205            let mut stack = stack.borrow_mut();
206            stack.push(name);
207            func();
208            stack.pop();
209        });
210    }
211}
212
213/// Debug the current thread' stack.
214pub fn debug() {
215    STACK.with(|stack| {
216        stack.borrow().debug();
217    });
218}
219
220#[inline]
221fn is_enabled() -> bool {
222    unsafe { ENABLED }
223}
224
225/// Enable performance profiling, this function is unsafe for now because you need to call
226/// it once before any profiling.
227#[inline]
228pub unsafe fn enable() {
229    ENABLED = true;
230}
231
232/// Disable performance profiling, this function is unsafe for now because you need to call
233/// it once before any profiling.
234#[inline]
235pub unsafe fn disable() {
236    ENABLED = false;
237}