how_to_use_profiler/how_to_use_profiler.rs
1use altius_benchtools::profiler;
2use std::{fs, thread};
3
4/// Performs an expensive calculation for benchmarking purposes.
5///
6/// This function simulates a CPU-intensive task by performing repeated
7/// modular exponentiation operations with a large prime modulus.
8///
9/// # Arguments
10///
11/// * `seed` - A seed value that affects the initial state of the calculation
12/// * `repeat` - The number of iterations to perform
13///
14/// # Returns
15///
16/// The final result of the calculation after repeated squaring operations
17fn expensive_calculation(seed: u128, repeat: u128) -> u128 {
18 let modulus: u128 = 18446744073709551617;
19 let mut base: u128 = 12142100291992418551 + seed;
20 for _ in 0..repeat {
21 base = (base * base) % modulus;
22 }
23 base
24}
25
26/// Demonstrates profiling different tasks across multiple threads.
27///
28/// This example shows how to use thread-specific profiling functions to track
29/// the execution of distinct tasks running in separate threads. Each task is
30/// uniquely identified by its name and thread ID.
31///
32/// The pattern used here is appropriate when:
33/// - You need to track multiple distinct tasks across different threads
34/// - Each task needs its own timing and metadata
35/// - Tasks are uniquely identified by both name and thread ID
36///
37/// # Arguments
38///
39/// * `output_path` - Path where the JSON profiling data will be saved
40///
41/// # Notes
42///
43/// * Each task must have a unique name within its thread
44/// * A task must be ended in the same thread where it was started
45/// * Tasks cannot be nested with the same name in the same thread
46///
47/// # Example Output
48///
49/// * See `outputs/profiler-output-0.json` after running the example
50fn different_task_in_different_threads(output_path: &str) {
51 profiler::clear();
52 let thread_num = 3;
53 let sub_task_num = 5;
54 let mut handles = vec![];
55
56 for thread_id in 0..thread_num {
57 let handle = thread::spawn(move || {
58 for task_id in 0..sub_task_num {
59 let seed = thread_id * sub_task_num + task_id;
60 let task_name = format!("task-{}-{}", thread_id, task_id);
61 profiler::start(task_name.as_str());
62 profiler::note_str(
63 task_name.as_str(),
64 "extra-descreption",
65 format!("You can add any extra description here for {}", task_name).as_str(),
66 );
67 expensive_calculation(seed as u128, 10000);
68 profiler::end(task_name.as_str());
69 }
70 });
71 handles.push(handle);
72 }
73
74 for handle in handles {
75 handle.join().unwrap();
76 }
77 profiler::dump_json(output_path);
78}
79
80/// Demonstrates profiling tasks across multiple threads with global task tracking.
81///
82/// This example shows how to use thread-agnostic profiling functions to track
83/// tasks that may be executed across different threads. Each task is identified
84/// by its base name, and an auto-incremented index is appended to distinguish
85/// multiple instances of the same task.
86///
87/// The pattern used here is appropriate when:
88/// - You need to track tasks that may span multiple threads
89/// - You want to reuse the same task name multiple times
90/// - Task identity is more important than which thread executed it
91///
92/// # Arguments
93///
94/// * `output_path` - Path where the JSON profiling data will be saved
95///
96/// # Notes
97///
98/// * Tasks with the same name are automatically given unique identifiers
99/// * A task must be ended before starting another with the same name
100/// * Each task instance is tracked with an auto-incremented ID in the output
101/// * All entries are recorded in the "main" thread regardless of actual thread
102fn global_task_ignore_thread(output_path: &str) {
103 profiler::clear();
104 let thread_num = 3;
105 let sub_task_num = 5;
106 let mut handles = vec![];
107
108 for thread_id in 0..thread_num {
109 let handle = thread::spawn(move || {
110 for task_id in 0..sub_task_num {
111 let seed = thread_id * sub_task_num + task_id;
112 let task_name = format!("task-in-thread-{}", thread_id);
113 profiler::start_multi(task_name.as_str());
114 profiler::note_str_multi(
115 task_name.as_str(),
116 "seed",
117 format!("The seed for this task is {}", seed).as_str(),
118 );
119 expensive_calculation(seed as u128, 10000);
120 profiler::end_multi(task_name.as_str());
121 }
122 });
123 handles.push(handle);
124 }
125
126 for handle in handles {
127 handle.join().unwrap();
128 }
129 profiler::dump_json(output_path);
130}
131
132/// Demonstrates recording profiling data without explicit start/end markers.
133///
134/// This example shows how to use the unchecked profiling functions to record
135/// data without following the normal start/end task flow. This is useful for
136/// recording arbitrary data points or results without timing specific tasks.
137///
138/// The pattern used here is appropriate when:
139/// - You need to record data points without timing concerns
140/// - You want to collect results or metrics from various operations
141/// - You don't need the strict start/end task structure
142///
143/// # Arguments
144///
145/// * `output_path` - Path where the JSON profiling data will be saved
146///
147/// # Notes
148///
149/// * This approach bypasses the normal task flow and safety checks
150/// * Useful for recording results or metrics without timing concerns
151/// * Creates task entries automatically if they don't exist
152/// * All entries are recorded in the "main" thread regardless of actual thread
153///
154/// # Safety
155///
156/// This function uses `note_str_unchecked` which:
157/// - Does not verify if the task exists
158/// - Creates a new task entry if none exists
159/// - Does not enforce the normal start/end task flow
160fn global_record_ignore_start_and_end(output_path: &str) {
161 profiler::clear();
162 let thread_num = 3;
163 let sub_task_num = 5;
164 let mut handles = vec![];
165
166 for thread_id in 0..thread_num {
167 let handle = thread::spawn(move || {
168 for task_id in 0..sub_task_num {
169 let seed = thread_id * sub_task_num + task_id;
170 let result = expensive_calculation(seed as u128, 10000);
171 profiler::note_str_unchecked(
172 "result-for-tasks",
173 format!("seed-{}", seed).as_str(),
174 result.to_string().as_str(),
175 );
176 }
177 });
178 handles.push(handle);
179 }
180
181 for handle in handles {
182 handle.join().unwrap();
183 }
184 profiler::dump_json(output_path);
185}
186
187/// Runs all profiler examples and saves the output to JSON files.
188///
189/// This function demonstrates the three main profiling patterns:
190/// 1. Thread-specific task profiling - Records distinct tasks in different threads
191/// 2. Global task profiling across threads - Records tasks with auto-incremented IDs
192/// 3. Unchecked data recording without start/end markers - Records data points without timing
193///
194/// Each example generates a separate JSON output file in the "outputs" directory.
195fn main() {
196 fs::create_dir_all("outputs").unwrap();
197 different_task_in_different_threads("outputs/profiler-output-0.json");
198 global_task_ignore_thread("outputs/profiler-output-1.json");
199 global_record_ignore_start_and_end("outputs/profiler-output-2.json");
200}