snarkvm_profiler/
lib.rs

1// Copyright (C) 2019-2021 Aleo Systems Inc.
2// This file is part of the snarkVM library.
3
4// The snarkVM library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The snarkVM library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the snarkVM library. If not, see <https://www.gnu.org/licenses/>.
16
17#![allow(unused_imports)]
18pub use inner::*;
19
20#[cfg(feature = "print-trace")]
21#[macro_use]
22pub mod inner {
23    use std::{sync::atomic::AtomicUsize, time::Instant};
24
25    pub use colored::Colorize;
26
27    pub static NUM_INDENT: AtomicUsize = AtomicUsize::new(0);
28    pub const PAD_CHAR: &str = "·";
29
30    pub struct TimerInfo {
31        pub msg: String,
32        pub time: Instant,
33    }
34
35    #[macro_export]
36    macro_rules! start_timer {
37        ($msg:expr) => {{
38            use std::{sync::atomic::Ordering, time::Instant};
39            use $crate::{compute_indent, Colorize, NUM_INDENT, PAD_CHAR};
40
41            let msg = $msg();
42            let start_info = "Start:".yellow().bold();
43            let indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed);
44            let indent = compute_indent(indent_amount);
45
46            println!("{}{:8} {}", indent, start_info, msg);
47            NUM_INDENT.fetch_add(1, Ordering::Relaxed);
48            $crate::TimerInfo {
49                msg: msg.to_string(),
50                time: Instant::now(),
51            }
52        }};
53    }
54
55    #[macro_export]
56    macro_rules! end_timer {
57        ($time:expr) => {{
58            end_timer!($time, || "");
59        }};
60        ($time:expr, $msg:expr) => {{
61            use std::sync::atomic::Ordering;
62            use $crate::{compute_indent, Colorize, NUM_INDENT, PAD_CHAR};
63
64            let time = $time.time;
65            let final_time = time.elapsed();
66            let final_time = {
67                let secs = final_time.as_secs();
68                let millis = final_time.subsec_millis();
69                let micros = final_time.subsec_micros() % 1000;
70                let nanos = final_time.subsec_nanos() % 1000;
71                if secs != 0 {
72                    format!("{}.{}s", secs, millis).bold()
73                } else if millis > 0 {
74                    format!("{}.{}ms", millis, micros).bold()
75                } else if micros > 0 {
76                    format!("{}.{}µs", micros, nanos).bold()
77                } else {
78                    format!("{}ns", final_time.subsec_nanos()).bold()
79                }
80            };
81
82            let end_info = "End:".green().bold();
83            let message = format!("{} {}", $time.msg, $msg());
84
85            NUM_INDENT.fetch_sub(1, Ordering::Relaxed);
86            let indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed);
87            let indent = compute_indent(indent_amount);
88
89            // Todo: Recursively ensure that *entire* string is of appropriate
90            // width (not just message).
91            println!(
92                "{}{:8} {:.<pad$}{}",
93                indent,
94                end_info,
95                message,
96                final_time,
97                pad = 75 - indent_amount
98            );
99        }};
100    }
101
102    #[macro_export]
103    macro_rules! add_to_trace {
104        ($title:expr, $msg:expr) => {{
105            use std::sync::atomic::Ordering;
106            use $crate::{compute_indent, compute_indent_whitespace, Colorize, NUM_INDENT, PAD_CHAR};
107
108            let start_msg = "StartMsg".yellow().bold();
109            let end_msg = "EndMsg".green().bold();
110            let title = $title();
111            let start_msg = format!("{}: {}", start_msg, title);
112            let end_msg = format!("{}: {}", end_msg, title);
113
114            let start_indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed);
115            let start_indent = compute_indent(start_indent_amount);
116
117            let msg_indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed) + 2;
118            let msg_indent = compute_indent_whitespace(msg_indent_amount);
119            let mut final_message = "\n".to_string();
120            for line in $msg().lines() {
121                final_message += &format!("{}{}\n", msg_indent, line,);
122            }
123
124            // Todo: Recursively ensure that *entire* string is of appropriate
125            // width (not just message).
126            println!("{}{}", start_indent, start_msg);
127            println!("{}{}", msg_indent, final_message,);
128            println!("{}{}", start_indent, end_msg);
129        }};
130    }
131
132    pub fn compute_indent_whitespace(indent_amount: usize) -> String {
133        let mut indent = String::new();
134        for _ in 0..indent_amount {
135            indent.push(' ');
136        }
137        indent
138    }
139
140    pub fn compute_indent(indent_amount: usize) -> String {
141        use std::env::var;
142        let mut indent = String::new();
143        let pad_string = match var("CLICOLOR") {
144            Ok(val) => {
145                if val == "0" {
146                    " "
147                } else {
148                    PAD_CHAR
149                }
150            }
151            Err(_) => PAD_CHAR,
152        };
153        for _ in 0..indent_amount {
154            indent.push_str(&pad_string.white());
155        }
156        indent
157    }
158}
159
160#[cfg(not(feature = "print-trace"))]
161#[macro_use]
162mod inner {
163    pub struct TimerInfo;
164
165    #[macro_export]
166    macro_rules! start_timer {
167        ($msg:expr) => {
168            $crate::TimerInfo
169        };
170    }
171    #[macro_export]
172    macro_rules! add_to_trace {
173        ($title:expr, $msg:expr) => {
174            let _ = $msg;
175        };
176    }
177
178    #[macro_export]
179    macro_rules! end_timer {
180        ($time:expr, $msg:expr) => {
181            let _ = $msg;
182            let _ = $time;
183        };
184        ($time:expr) => {
185            let _ = $time;
186        };
187    }
188}
189
190mod tests {
191    use super::*;
192
193    #[test]
194    fn print_start_end() {
195        let start = start_timer!(|| "Hello");
196        end_timer!(start);
197    }
198
199    #[test]
200    fn print_add() {
201        let start = start_timer!(|| "Hello");
202        add_to_trace!(|| "HelloMsg", || "Hello, I\nAm\nA\nMessage");
203        end_timer!(start);
204    }
205}