1#[cfg(feature = "profiling")]
13mod inner {
14 use std::sync::atomic::{AtomicU64, Ordering};
15 use std::time::Instant;
16
17 pub struct ProfileCounters {
20 pub dequant_transform_ns: AtomicU64,
21 pub dequant_transform_calls: AtomicU64,
22 pub dct_ns: AtomicU64,
23 pub dct_calls: AtomicU64,
24 pub entropy_decode_ns: AtomicU64,
25 pub entropy_decode_calls: AtomicU64,
26 pub chroma_upsample_ns: AtomicU64,
27 pub chroma_upsample_calls: AtomicU64,
28 pub render_pipeline_ns: AtomicU64,
29 pub render_pipeline_calls: AtomicU64,
30 pub modular_decode_ns: AtomicU64,
31 pub modular_decode_calls: AtomicU64,
32 }
33
34 impl ProfileCounters {
35 pub const fn new() -> Self {
36 Self {
37 dequant_transform_ns: AtomicU64::new(0),
38 dequant_transform_calls: AtomicU64::new(0),
39 dct_ns: AtomicU64::new(0),
40 dct_calls: AtomicU64::new(0),
41 entropy_decode_ns: AtomicU64::new(0),
42 entropy_decode_calls: AtomicU64::new(0),
43 chroma_upsample_ns: AtomicU64::new(0),
44 chroma_upsample_calls: AtomicU64::new(0),
45 render_pipeline_ns: AtomicU64::new(0),
46 render_pipeline_calls: AtomicU64::new(0),
47 modular_decode_ns: AtomicU64::new(0),
48 modular_decode_calls: AtomicU64::new(0),
49 }
50 }
51
52 #[allow(dead_code)] pub fn reset(&self) {
54 self.dequant_transform_ns.store(0, Ordering::Relaxed);
55 self.dequant_transform_calls.store(0, Ordering::Relaxed);
56 self.dct_ns.store(0, Ordering::Relaxed);
57 self.dct_calls.store(0, Ordering::Relaxed);
58 self.entropy_decode_ns.store(0, Ordering::Relaxed);
59 self.entropy_decode_calls.store(0, Ordering::Relaxed);
60 self.chroma_upsample_ns.store(0, Ordering::Relaxed);
61 self.chroma_upsample_calls.store(0, Ordering::Relaxed);
62 self.render_pipeline_ns.store(0, Ordering::Relaxed);
63 self.render_pipeline_calls.store(0, Ordering::Relaxed);
64 self.modular_decode_ns.store(0, Ordering::Relaxed);
65 self.modular_decode_calls.store(0, Ordering::Relaxed);
66 }
67 }
68
69 pub static COUNTERS: ProfileCounters = ProfileCounters::new();
70
71 pub struct ProfileGuard {
73 start: Instant,
74 ns_counter: &'static AtomicU64,
75 call_counter: &'static AtomicU64,
76 }
77
78 impl ProfileGuard {
79 #[inline]
80 pub fn new(ns_counter: &'static AtomicU64, call_counter: &'static AtomicU64) -> Self {
81 Self {
82 start: Instant::now(),
83 ns_counter,
84 call_counter,
85 }
86 }
87 }
88
89 impl Drop for ProfileGuard {
90 #[inline]
91 fn drop(&mut self) {
92 let elapsed = self.start.elapsed().as_nanos() as u64;
93 let ns = self.ns_counter.load(Ordering::Relaxed);
97 self.ns_counter.store(ns + elapsed, Ordering::Relaxed);
98 let calls = self.call_counter.load(Ordering::Relaxed);
99 self.call_counter.store(calls + 1, Ordering::Relaxed);
100 }
101 }
102
103 pub fn print_profile_report() {
105 let c = &COUNTERS;
106
107 eprintln!("\n=== JXL-RS Profile Report ===\n");
108
109 let entries = [
110 (
111 "dequant_transform",
112 c.dequant_transform_ns.load(Ordering::Relaxed),
113 c.dequant_transform_calls.load(Ordering::Relaxed),
114 ),
115 (
116 "dct/idct",
117 c.dct_ns.load(Ordering::Relaxed),
118 c.dct_calls.load(Ordering::Relaxed),
119 ),
120 (
121 "entropy_decode",
122 c.entropy_decode_ns.load(Ordering::Relaxed),
123 c.entropy_decode_calls.load(Ordering::Relaxed),
124 ),
125 (
126 "chroma_upsample",
127 c.chroma_upsample_ns.load(Ordering::Relaxed),
128 c.chroma_upsample_calls.load(Ordering::Relaxed),
129 ),
130 (
131 "render_pipeline",
132 c.render_pipeline_ns.load(Ordering::Relaxed),
133 c.render_pipeline_calls.load(Ordering::Relaxed),
134 ),
135 (
136 "modular_decode",
137 c.modular_decode_ns.load(Ordering::Relaxed),
138 c.modular_decode_calls.load(Ordering::Relaxed),
139 ),
140 ];
141
142 let total_ns: u64 = entries.iter().map(|(_, ns, _)| ns).sum();
143
144 eprintln!(
145 "{:<20} {:>12} {:>12} {:>10} {:>8}",
146 "Function", "Time (ms)", "Calls", "Avg (µs)", "% Total"
147 );
148 eprintln!("{:-<66}", "");
149
150 for (name, ns, calls) in entries {
151 if calls > 0 {
152 let ms = ns as f64 / 1_000_000.0;
153 let avg_us = (ns as f64 / calls as f64) / 1_000.0;
154 let pct = if total_ns > 0 {
155 (ns as f64 / total_ns as f64) * 100.0
156 } else {
157 0.0
158 };
159 eprintln!(
160 "{:<20} {:>12.2} {:>12} {:>10.2} {:>7.1}%",
161 name, ms, calls, avg_us, pct
162 );
163 }
164 }
165
166 eprintln!("{:-<66}", "");
167 eprintln!("{:<20} {:>12.2}", "TOTAL", total_ns as f64 / 1_000_000.0);
168 eprintln!();
169 }
170
171 #[allow(dead_code)] pub fn reset_profile_counters() {
174 COUNTERS.reset();
175 }
176}
177
178#[cfg(not(feature = "profiling"))]
179#[allow(dead_code)]
180mod inner {
181 #[derive(Default)]
183 pub struct ProfileGuard;
184
185 impl ProfileGuard {
186 #[inline(always)]
187 pub fn new() -> Self {
188 Self
189 }
190 }
191
192 #[inline(always)]
193 pub fn print_profile_report() {}
194
195 #[inline(always)]
196 pub fn reset_profile_counters() {}
197}
198
199#[allow(unused_imports)]
200pub use inner::*;
201
202#[cfg(feature = "profiling")]
205#[macro_export]
206macro_rules! profile {
207 (dequant_transform) => {
208 let _guard = $crate::util::ProfileGuard::new(
209 &$crate::util::COUNTERS.dequant_transform_ns,
210 &$crate::util::COUNTERS.dequant_transform_calls,
211 );
212 };
213 (dct) => {
214 let _guard = $crate::util::ProfileGuard::new(
215 &$crate::util::COUNTERS.dct_ns,
216 &$crate::util::COUNTERS.dct_calls,
217 );
218 };
219 (entropy_decode) => {
220 let _guard = $crate::util::ProfileGuard::new(
221 &$crate::util::COUNTERS.entropy_decode_ns,
222 &$crate::util::COUNTERS.entropy_decode_calls,
223 );
224 };
225 (chroma_upsample) => {
226 let _guard = $crate::util::ProfileGuard::new(
227 &$crate::util::COUNTERS.chroma_upsample_ns,
228 &$crate::util::COUNTERS.chroma_upsample_calls,
229 );
230 };
231 (render_pipeline) => {
232 let _guard = $crate::util::ProfileGuard::new(
233 &$crate::util::COUNTERS.render_pipeline_ns,
234 &$crate::util::COUNTERS.render_pipeline_calls,
235 );
236 };
237 (modular_decode) => {
238 let _guard = $crate::util::ProfileGuard::new(
239 &$crate::util::COUNTERS.modular_decode_ns,
240 &$crate::util::COUNTERS.modular_decode_calls,
241 );
242 };
243}
244
245#[cfg(not(feature = "profiling"))]
246#[macro_export]
247macro_rules! profile {
248 ($name:ident) => {};
249}