Skip to main content

aleo_std_profiler/
lib.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![allow(unused_imports)]
17pub use inner::*;
18
19#[cfg(feature = "profiler")]
20#[macro_use]
21pub mod inner {
22    use std::{sync::atomic::AtomicUsize, time::Instant};
23
24    pub use colored::Colorize;
25
26    pub static NUM_INDENT: AtomicUsize = AtomicUsize::new(0);
27    pub const PAD_CHAR: &str = "·";
28
29    pub struct TimerInfo {
30        pub msg: String,
31        pub time: Instant,
32    }
33
34    #[macro_export]
35    macro_rules! start_timer {
36        ($msg:expr) => {{
37            use std::{sync::atomic::Ordering, time::Instant};
38            use $crate::{Colorize, NUM_INDENT, PAD_CHAR, compute_indent};
39
40            let msg = $msg();
41            let start_info = "Start:".yellow().bold();
42            let indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed);
43            let indent = compute_indent(indent_amount);
44
45            println!("{}{:8} {}", indent, start_info, msg);
46            NUM_INDENT.fetch_add(1, Ordering::Relaxed);
47            $crate::TimerInfo {
48                msg: msg.to_string(),
49                time: Instant::now(),
50            }
51        }};
52    }
53
54    #[macro_export]
55    macro_rules! end_timer {
56        ($time:expr) => {{
57            end_timer!($time, || "");
58        }};
59        ($time:expr, $msg:expr) => {{
60            use std::sync::atomic::Ordering;
61            use $crate::{Colorize, NUM_INDENT, PAD_CHAR, compute_indent};
62
63            let time = $time.time;
64            let final_time = time.elapsed();
65            let final_time = {
66                let secs = final_time.as_secs();
67                let millis = final_time.subsec_millis();
68                let micros = final_time.subsec_micros() % 1000;
69                let nanos = final_time.subsec_nanos() % 1000;
70                if secs != 0 {
71                    format!("{}.{:0>3}s", secs, millis).bold()
72                } else if millis > 0 {
73                    format!("{}.{:0>3}ms", millis, micros).bold()
74                } else if micros > 0 {
75                    format!("{}.{:0>3}µs", micros, nanos).bold()
76                } else {
77                    format!("{}ns", final_time.subsec_nanos()).bold()
78                }
79            };
80
81            let end_info = "End:".green().bold();
82            let message = format!("{} {}", $time.msg, $msg());
83
84            NUM_INDENT.fetch_sub(1, Ordering::Relaxed);
85            let indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed);
86            let indent = compute_indent(indent_amount);
87
88            // Todo: Recursively ensure that *entire* string is of appropriate
89            // width (not just message).
90            println!(
91                "{}{:8} {:.<pad$}{}",
92                indent,
93                end_info,
94                message,
95                final_time,
96                pad = 75usize.saturating_sub(indent_amount)
97            );
98        }};
99    }
100
101    #[macro_export]
102    macro_rules! add_to_trace {
103        ($title:expr, $msg:expr) => {{
104            use std::sync::atomic::Ordering;
105            use $crate::{Colorize, NUM_INDENT, PAD_CHAR, compute_indent, compute_indent_whitespace};
106
107            let start_msg = "StartMsg".yellow().bold();
108            let end_msg = "EndMsg".green().bold();
109            let title = $title();
110            let start_msg = format!("{}: {}", start_msg, title);
111            let end_msg = format!("{}: {}", end_msg, title);
112
113            let start_indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed);
114            let start_indent = compute_indent(start_indent_amount);
115
116            let msg_indent_amount = 2 * NUM_INDENT.fetch_add(0, Ordering::Relaxed) + 2;
117            let msg_indent = compute_indent_whitespace(msg_indent_amount);
118            let mut final_message = "\n".to_string();
119            for line in $msg().lines() {
120                final_message += &format!("{}{}\n", msg_indent, line,);
121            }
122
123            // Todo: Recursively ensure that *entire* string is of appropriate
124            // width (not just message).
125            println!("{}{}", start_indent, start_msg);
126            println!("{}{}", msg_indent, final_message,);
127            println!("{}{}", start_indent, end_msg);
128        }};
129    }
130
131    pub fn compute_indent_whitespace(indent_amount: usize) -> String {
132        let mut indent = String::new();
133        for _ in 0..indent_amount {
134            indent.push(' ');
135        }
136        indent
137    }
138
139    pub fn compute_indent(indent_amount: usize) -> String {
140        use std::env::var;
141        let mut indent = String::new();
142        let pad_string = match var("CLICOLOR") {
143            Ok(val) => {
144                if val == "0" {
145                    " "
146                } else {
147                    PAD_CHAR
148                }
149            }
150            Err(_) => PAD_CHAR,
151        };
152        for _ in 0..indent_amount {
153            indent.push_str(&pad_string.white());
154        }
155        indent
156    }
157}
158
159#[cfg(not(feature = "profiler"))]
160#[macro_use]
161mod inner {
162    pub struct TimerInfo;
163
164    #[macro_export]
165    macro_rules! start_timer {
166        ($msg:expr) => {
167            $crate::TimerInfo
168        };
169    }
170    #[macro_export]
171    macro_rules! add_to_trace {
172        ($title:expr, $msg:expr) => {
173            let _ = $msg;
174        };
175    }
176
177    #[macro_export]
178    macro_rules! end_timer {
179        ($time:expr, $msg:expr) => {
180            let _ = $msg;
181            let _ = $time;
182        };
183        ($time:expr) => {
184            let _ = $time;
185        };
186    }
187}
188
189mod tests {
190    use super::*;
191
192    #[test]
193    fn print_start_end() {
194        let start = start_timer!(|| "Hello");
195        end_timer!(start);
196    }
197
198    #[test]
199    fn print_add() {
200        let start = start_timer!(|| "Hello");
201        add_to_trace!(|| "HelloMsg", || "Hello, I\nAm\nA\nMessage");
202        end_timer!(start);
203    }
204
205    #[test]
206    fn print_nested_message() {
207        let mut starts = vec![];
208        for _ in 0..100 {
209            starts.push(start_timer!(|| "Hello"));
210        }
211        add_to_trace!(|| "HelloMsg", || "Hello, I\nAm\nA\nMessage");
212        for i in 0..100 {
213            end_timer!(starts[i]);
214        }
215    }
216}