Skip to main content

torsh_profiler/
macros.rs

1//! Convenient macros for profiling operations
2//!
3//! This module provides easy-to-use macros that automatically insert profiling code
4//! without requiring manual setup of RAII guards or function calls.
5
6/// Profile a block of code with automatic naming
7///
8/// # Examples
9///
10/// ```rust
11/// use torsh_profiler::profile_block;
12///
13/// fn my_function() {
14///     let result = profile_block!("matrix_multiplication", {
15///         // Your code here
16///         42i32 // Some computation result
17///     });
18/// }
19/// ```
20#[macro_export]
21macro_rules! profile_block {
22    ($name:expr, $block:block) => {{
23        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "block".to_string());
24        $block
25    }};
26    ($name:expr, $category:expr, $block:block) => {{
27        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), $category.to_string());
28        $block
29    }};
30}
31
32/// Profile the current function automatically
33///
34/// # Examples
35///
36/// ```rust
37/// use torsh_profiler::profile_current_function;
38///
39/// fn my_expensive_function(x: i32) -> i32 {
40///     profile_current_function!();
41///     // This function will be automatically profiled
42///     x * x
43/// }
44///
45/// fn my_computation(data: &[f32]) -> f32 {
46///     profile_current_function!("computation");
47///     // This function will be profiled under "computation" category
48///     data.iter().sum()
49/// }
50/// ```
51#[macro_export]
52macro_rules! profile_current_function {
53    () => {
54        let _guard = $crate::cpu::ProfileScope::simple(
55            format!("{}::function", module_path!()),
56            "function".to_string(),
57        );
58    };
59    ($category:expr) => {
60        let _guard = $crate::cpu::ProfileScope::simple(
61            format!("{}::function", module_path!()),
62            $category.to_string(),
63        );
64    };
65}
66
67/// Profile a closure with optional name and category
68///
69/// # Examples
70///
71/// ```rust
72/// use torsh_profiler::profile_closure;
73///
74/// let result = profile_closure!("data_processing", || {
75///     42i32 // Some computation result
76/// });
77///
78/// let result = profile_closure!("data_processing", "computation", || {
79///     42i32 // Some computation result
80/// });
81/// ```
82#[macro_export]
83macro_rules! profile_closure {
84    ($name:expr, $closure:expr) => {{
85        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "closure".to_string());
86        $closure()
87    }};
88    ($name:expr, $category:expr, $closure:expr) => {{
89        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), $category.to_string());
90        $closure()
91    }};
92}
93
94/// Profile memory allocation with tracking
95///
96/// # Examples
97///
98/// ```rust
99/// use torsh_profiler::profile_alloc;
100///
101/// let vec: Vec<i32> = profile_alloc!("large_vector", {
102///     Vec::with_capacity(1000000)
103/// });
104/// ```
105#[macro_export]
106macro_rules! profile_alloc {
107    ($name:expr, $block:block) => {{
108        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "allocation".to_string());
109        $block
110    }};
111}
112
113/// Profile CUDA operations
114///
115/// # Examples
116///
117/// ```rust
118/// use torsh_profiler::profile_cuda;
119///
120/// profile_cuda!("kernel_launch", {
121///     // CUDA kernel launch code
122///     let _result = 42i32; // Some GPU computation
123/// });
124/// ```
125#[macro_export]
126macro_rules! profile_cuda {
127    ($name:expr, $block:block) => {{
128        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "cuda".to_string());
129        $block
130    }};
131}
132
133/// Profile tensor operations with automatic FLOPS counting
134///
135/// # Examples
136///
137/// ```rust
138/// use torsh_profiler::profile_tensor_op;
139///
140/// let m = 100u64;
141/// let n = 100u64;
142/// let k = 100u64;
143/// let result = profile_tensor_op!("matrix_multiply", flops: 2 * m * n * k, {
144///     // Matrix multiplication code
145///     42i32 // Some computation result
146/// });
147/// ```
148#[macro_export]
149macro_rules! profile_tensor_op {
150    ($name:expr, flops: $flops:expr, $block:block) => {{
151        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "tensor_op".to_string());
152        $block
153    }};
154}
155
156/// Conditionally profile based on a feature flag or condition
157///
158/// # Examples
159///
160/// ```rust
161/// use torsh_profiler::profile_if;
162///
163/// // Only profile if in debug mode
164/// profile_if!(cfg!(debug_assertions), "debug_operation", {
165///     let _result = 42i32; // Some operation
166/// });
167///
168/// // Only profile based on a condition
169/// profile_if!(true, "always_profile", {
170///     let _result = 42i32; // Some operation
171/// });
172/// ```
173#[macro_export]
174macro_rules! profile_if {
175    ($condition:expr, $name:expr, $block:block) => {{
176        if $condition {
177            let _guard =
178                $crate::cpu::ProfileScope::simple($name.to_string(), "conditional".to_string());
179            $block
180        } else {
181            $block
182        }
183    }};
184}
185
186/// Profile with custom metadata
187///
188/// # Examples
189///
190/// ```rust
191/// use torsh_profiler::profile_with_metadata;
192///
193/// let data = vec![1, 2, 3, 4];
194/// let result = profile_with_metadata!("data_processing", {
195///     operation_count: 1000u64,
196///     bytes_transferred: data.len() as u64,
197///     flops: 50000u64
198/// }, {
199///     // Process data
200///     42i32 // Some result
201/// });
202/// ```
203#[macro_export]
204macro_rules! profile_with_metadata {
205    ($name:expr, { $($key:ident: $value:expr),* $(,)? }, $block:block) => {{
206        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "metadata".to_string());
207        $block
208    }};
209}
210
211/// Profile loop iterations with automatic batching
212///
213/// # Examples
214///
215/// ```rust
216/// use torsh_profiler::profile_loop;
217///
218/// let data = vec![1, 2, 3, 4];
219/// profile_loop!("data_processing", for item in data.iter(), {
220///     let _result = item * 2; // Some processing
221/// });
222/// ```
223#[macro_export]
224macro_rules! profile_loop {
225    ($name:expr, for $item:ident in $iter:expr, $body:block) => {{
226        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "loop".to_string());
227        for $item in $iter $body
228    }};
229}
230
231/// Profile async operations
232///
233/// # Examples
234///
235/// ```rust
236/// use torsh_profiler::profile_async;
237/// use std::future::Future;
238///
239/// async fn my_async_function() {
240///     let result = profile_async!("async_operation", async {
241///         // Your async code here
242///         42i32
243///     }).await;
244/// }
245/// ```
246#[macro_export]
247macro_rules! profile_async {
248    ($name:expr, $future:expr) => {{
249        let _guard = $crate::cpu::ProfileScope::simple($name.to_string(), "async".to_string());
250        $future
251    }};
252}
253
254/// Benchmark and profile comparison between different implementations
255///
256/// # Examples
257///
258/// ```rust
259/// use torsh_profiler::profile_compare;
260///
261/// let mut data = vec![3, 1, 4, 1, 5];
262/// profile_compare!("sorting_algorithms", {
263///     "quicksort" => { data.clone().sort_unstable(); },
264///     "mergesort" => { data.clone().sort(); }
265/// });
266/// ```
267#[macro_export]
268macro_rules! profile_compare {
269    ($name:expr, { $($variant:expr => $block:block),* $(,)? }) => {{
270        let mut results = std::collections::HashMap::new();
271
272        $(
273            let start_time = std::time::Instant::now();
274            {
275                let _guard = $crate::cpu::ProfileScope::simple(
276                    format!("{}_{}", $name, $variant),
277                    "comparison".to_string()
278                );
279                $block
280            }
281            let duration = start_time.elapsed();
282            results.insert($variant.to_string(), duration);
283        )*
284
285        // Log comparison results
286        println!("Profile comparison for {}:", $name);
287        let mut sorted_results: Vec<_> = results.iter().collect();
288        sorted_results.sort_by(|a, b| a.1.cmp(b.1));
289
290        for (variant, duration) in sorted_results {
291            println!("  {}: {:?}", variant, duration);
292        }
293
294        results
295    }};
296}
297
298/// Create a profiling scope with automatic cleanup
299///
300/// # Examples
301///
302/// ```rust
303/// use torsh_profiler::profiling_scope;
304///
305/// fn my_function() {
306///     profiling_scope!("my_function", "computation");
307///
308///     // All code in this function will be profiled
309///     let _result = 42i32; // Some computation
310/// }
311/// ```
312#[macro_export]
313macro_rules! profiling_scope {
314    ($name:expr) => {
315        let _profiling_guard =
316            $crate::cpu::ProfileScope::simple($name.to_string(), "scope".to_string());
317    };
318    ($name:expr, $category:expr) => {
319        let _profiling_guard =
320            $crate::cpu::ProfileScope::simple($name.to_string(), $category.to_string());
321    };
322}
323
324/// Profile with automatic overhead measurement
325///
326/// # Examples
327///
328/// ```rust
329/// use torsh_profiler::{profile_with_overhead, ProfileResult};
330///
331/// let (result, overhead) = profile_with_overhead!("operation", {
332///     42i32 // Some computation
333/// });
334///
335/// println!("Operation completed in {:?}, profiling overhead: {:?}",
336///          result.duration, overhead);
337/// ```
338#[macro_export]
339macro_rules! profile_with_overhead {
340    ($name:expr, $block:block) => {{
341        let overhead_start = std::time::Instant::now();
342        let _guard =
343            $crate::cpu::ProfileScope::simple($name.to_string(), "overhead_measured".to_string());
344        let setup_overhead = overhead_start.elapsed();
345
346        let operation_start = std::time::Instant::now();
347        let result = $block;
348        let operation_duration = operation_start.elapsed();
349
350        let teardown_start = std::time::Instant::now();
351        drop(_guard);
352        let teardown_overhead = teardown_start.elapsed();
353
354        let total_overhead = setup_overhead + teardown_overhead;
355
356        (
357            ProfileResult {
358                result,
359                duration: operation_duration,
360            },
361            total_overhead,
362        )
363    }};
364}
365
366/// Helper struct for profile_with_overhead macro
367pub struct ProfileResult<T> {
368    pub result: T,
369    pub duration: std::time::Duration,
370}
371
372/// Profile with sampling (only profile every N calls)
373///
374/// # Examples
375///
376/// ```rust
377/// use torsh_profiler::profile_sampled;
378///
379/// // Only profile every 100th call
380/// profile_sampled!("frequent_operation", sample_rate: 100, {
381///     let _result = 42i32; // Some operation
382/// });
383/// ```
384#[macro_export]
385macro_rules! profile_sampled {
386    ($name:expr, sample_rate: $rate:expr, $block:block) => {{
387        use std::sync::atomic::{AtomicUsize, Ordering};
388        static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
389
390        let call_num = CALL_COUNT.fetch_add(1, Ordering::Relaxed);
391
392        if call_num % $rate == 0 {
393            let _guard = $crate::cpu::ProfileScope::simple(
394                format!("{}_sample_{}", $name, call_num / $rate),
395                "sampled".to_string(),
396            );
397            $block
398        } else {
399            $block
400        }
401    }};
402}
403
404/// Profile with thread-local storage for reduced overhead
405///
406/// # Examples
407///
408/// ```rust
409/// use torsh_profiler::profile_thread_local;
410///
411/// profile_thread_local!("thread_operation", {
412///     // This uses thread-local profiling to reduce contention
413///     let _result = 42i32; // Some work
414/// });
415/// ```
416#[macro_export]
417macro_rules! profile_thread_local {
418    ($name:expr, $block:block) => {{
419        thread_local! {
420            static PROFILER: std::cell::RefCell<$crate::cpu::CpuProfiler> =
421                std::cell::RefCell::new($crate::cpu::CpuProfiler::new());
422        }
423
424        let start_time = std::time::Instant::now();
425        let result = $block;
426        let duration = start_time.elapsed();
427
428        PROFILER.with(|profiler| {
429            let mut profiler = profiler.borrow_mut();
430            let _ = profiler.record_event($name, "thread_local", duration);
431        });
432
433        result
434    }};
435}
436
437// Automatic profiling based on function attributes (requires procedural macro)
438// This is a placeholder for the procedural macro implementation
439// pub use torsh_profiler_macros::*; // Not available - using inline macros instead
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444    use std::time::Duration;
445
446    #[test]
447    fn test_profile_block_macro() {
448        let result = profile_block!("test_block", {
449            std::thread::sleep(Duration::from_millis(1));
450            42
451        });
452        assert_eq!(result, 42);
453    }
454
455    #[test]
456    fn test_profile_closure_macro() {
457        let result = profile_closure!("test_closure", || {
458            std::thread::sleep(Duration::from_millis(1));
459            "success"
460        });
461        assert_eq!(result, "success");
462    }
463
464    #[test]
465    fn test_profile_if_macro() {
466        let result = profile_if!(true, "conditional_test", {
467            std::thread::sleep(Duration::from_millis(1));
468            "executed"
469        });
470        assert_eq!(result, "executed");
471
472        let result = profile_if!(false, "conditional_test", {
473            std::thread::sleep(Duration::from_millis(1));
474            "also_executed"
475        });
476        assert_eq!(result, "also_executed");
477    }
478
479    #[test]
480    fn test_profile_compare_macro() {
481        let mut data1 = vec![3, 1, 4, 1, 5, 9, 2, 6];
482        let mut data2 = data1.clone();
483
484        let results = profile_compare!("sorting_test", {
485            "sort" => { data1.sort(); },
486            "sort_unstable" => { data2.sort_unstable(); }
487        });
488
489        assert_eq!(results.len(), 2);
490        assert!(results.contains_key("sort"));
491        assert!(results.contains_key("sort_unstable"));
492    }
493
494    #[test]
495    fn test_profile_sampled_macro() {
496        let mut execution_count = 0;
497
498        for _ in 0..10 {
499            profile_sampled!("sampled_test", sample_rate: 3, {
500                execution_count += 1;
501            });
502        }
503
504        assert_eq!(execution_count, 10); // All executions should happen
505    }
506
507    #[test]
508    fn test_profile_with_overhead_macro() {
509        let (result, overhead) = profile_with_overhead!("overhead_test", {
510            std::thread::sleep(Duration::from_millis(1));
511            "test_result"
512        });
513
514        assert_eq!(result.result, "test_result");
515        assert!(result.duration >= Duration::from_millis(1));
516        assert!(overhead < Duration::from_millis(1)); // Overhead should be minimal
517    }
518
519    #[test]
520    fn test_profiling_scope_macro() {
521        profiling_scope!("test_scope");
522
523        // Scope should be active for the remainder of this function
524        std::thread::sleep(Duration::from_millis(1));
525    }
526
527    #[test]
528    fn test_profile_thread_local_macro() {
529        let result = profile_thread_local!("thread_local_test", {
530            std::thread::sleep(Duration::from_millis(1));
531            "thread_result"
532        });
533
534        assert_eq!(result, "thread_result");
535    }
536
537    #[tokio::test]
538    async fn test_profile_async_macro() {
539        let result = profile_async!("async_test", async {
540            tokio::time::sleep(Duration::from_millis(1)).await;
541            "async_result"
542        })
543        .await;
544
545        assert_eq!(result, "async_result");
546    }
547}