1#[derive(Debug, Clone, Default)]
10pub struct ExecutionStats {
11 pub instructions_executed: u64,
12 pub integer_ops: u64,
13 pub float_ops: u64,
14 pub memory_ops: u64,
15 pub control_ops: u64,
16 pub wave_ops: u64,
17 pub atomic_ops: u64,
18
19 pub device_loads: u64,
20 pub device_load_bytes: u64,
21 pub device_stores: u64,
22 pub device_store_bytes: u64,
23
24 pub local_loads: u64,
25 pub local_load_bytes: u64,
26 pub local_stores: u64,
27 pub local_store_bytes: u64,
28
29 pub barriers: u64,
30 pub divergent_branches: u64,
31
32 pub workgroups_executed: u64,
33 pub waves_executed: u64,
34}
35
36impl ExecutionStats {
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn record_instruction(&mut self, category: InstructionCategory) {
42 self.instructions_executed += 1;
43 match category {
44 InstructionCategory::Integer => self.integer_ops += 1,
45 InstructionCategory::Float => self.float_ops += 1,
46 InstructionCategory::Memory => self.memory_ops += 1,
47 InstructionCategory::Control => self.control_ops += 1,
48 InstructionCategory::WaveOp => self.wave_ops += 1,
49 InstructionCategory::Atomic => self.atomic_ops += 1,
50 }
51 }
52
53 pub fn record_device_load(&mut self, bytes: u64) {
54 self.device_loads += 1;
55 self.device_load_bytes += bytes;
56 }
57
58 pub fn record_device_store(&mut self, bytes: u64) {
59 self.device_stores += 1;
60 self.device_store_bytes += bytes;
61 }
62
63 pub fn record_local_load(&mut self, bytes: u64) {
64 self.local_loads += 1;
65 self.local_load_bytes += bytes;
66 }
67
68 pub fn record_local_store(&mut self, bytes: u64) {
69 self.local_stores += 1;
70 self.local_store_bytes += bytes;
71 }
72
73 pub fn record_barrier(&mut self) {
74 self.barriers += 1;
75 }
76
77 pub fn record_divergent_branch(&mut self) {
78 self.divergent_branches += 1;
79 }
80
81 pub fn record_wave(&mut self) {
82 self.waves_executed += 1;
83 }
84
85 pub fn merge(&mut self, other: &ExecutionStats) {
86 self.instructions_executed += other.instructions_executed;
87 self.integer_ops += other.integer_ops;
88 self.float_ops += other.float_ops;
89 self.memory_ops += other.memory_ops;
90 self.control_ops += other.control_ops;
91 self.wave_ops += other.wave_ops;
92 self.atomic_ops += other.atomic_ops;
93
94 self.device_loads += other.device_loads;
95 self.device_load_bytes += other.device_load_bytes;
96 self.device_stores += other.device_stores;
97 self.device_store_bytes += other.device_store_bytes;
98
99 self.local_loads += other.local_loads;
100 self.local_load_bytes += other.local_load_bytes;
101 self.local_stores += other.local_stores;
102 self.local_store_bytes += other.local_store_bytes;
103
104 self.barriers += other.barriers;
105 self.divergent_branches += other.divergent_branches;
106
107 self.waves_executed += other.waves_executed;
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum InstructionCategory {
113 Integer,
114 Float,
115 Memory,
116 Control,
117 WaveOp,
118 Atomic,
119}
120
121pub struct TraceWriter {
122 enabled: bool,
123}
124
125impl TraceWriter {
126 pub fn new(enabled: bool) -> Self {
127 Self { enabled }
128 }
129
130 pub fn trace_instruction(&self, workgroup_id: [u32; 3], wave_id: u32, pc: u32, disasm: &str) {
131 if self.enabled {
132 eprintln!(
133 "wg({},{},{}) wave[{}] pc=0x{:04x}: {}",
134 workgroup_id[0], workgroup_id[1], workgroup_id[2], wave_id, pc, disasm
135 );
136 }
137 }
138
139 pub fn is_enabled(&self) -> bool {
140 self.enabled
141 }
142}
143
144impl Default for TraceWriter {
145 fn default() -> Self {
146 Self::new(false)
147 }
148}
149
150impl std::fmt::Display for ExecutionStats {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 writeln!(f, "Execution Statistics:")?;
153 writeln!(f, " Instructions executed: {}", self.instructions_executed)?;
154 writeln!(f, " Integer ops: {}", self.integer_ops)?;
155 writeln!(f, " Float ops: {}", self.float_ops)?;
156 writeln!(f, " Memory ops: {}", self.memory_ops)?;
157 writeln!(f, " Control ops: {}", self.control_ops)?;
158 writeln!(f, " Wave ops: {}", self.wave_ops)?;
159 writeln!(f, " Atomic ops: {}", self.atomic_ops)?;
160 writeln!(f)?;
161 writeln!(f, " Device memory:")?;
162 writeln!(
163 f,
164 " Loads: {} ({} bytes)",
165 self.device_loads, self.device_load_bytes
166 )?;
167 writeln!(
168 f,
169 " Stores: {} ({} bytes)",
170 self.device_stores, self.device_store_bytes
171 )?;
172 writeln!(f)?;
173 writeln!(f, " Local memory:")?;
174 writeln!(
175 f,
176 " Loads: {} ({} bytes)",
177 self.local_loads, self.local_load_bytes
178 )?;
179 writeln!(
180 f,
181 " Stores: {} ({} bytes)",
182 self.local_stores, self.local_store_bytes
183 )?;
184 writeln!(f)?;
185 writeln!(f, " Barriers: {}", self.barriers)?;
186 writeln!(f, " Divergent branches: {}", self.divergent_branches)?;
187 writeln!(f)?;
188 writeln!(f, " Workgroups executed: {}", self.workgroups_executed)?;
189 writeln!(f, " Waves executed: {}", self.waves_executed)?;
190 Ok(())
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_stats_record_instruction() {
200 let mut stats = ExecutionStats::new();
201 stats.record_instruction(InstructionCategory::Integer);
202 stats.record_instruction(InstructionCategory::Float);
203 stats.record_instruction(InstructionCategory::Float);
204
205 assert_eq!(stats.instructions_executed, 3);
206 assert_eq!(stats.integer_ops, 1);
207 assert_eq!(stats.float_ops, 2);
208 }
209
210 #[test]
211 fn test_stats_merge() {
212 let mut stats1 = ExecutionStats::new();
213 stats1.instructions_executed = 100;
214 stats1.device_loads = 50;
215
216 let mut stats2 = ExecutionStats::new();
217 stats2.instructions_executed = 200;
218 stats2.device_loads = 30;
219
220 stats1.merge(&stats2);
221
222 assert_eq!(stats1.instructions_executed, 300);
223 assert_eq!(stats1.device_loads, 80);
224 }
225
226 #[test]
227 fn test_stats_display() {
228 let stats = ExecutionStats::new();
229 let output = format!("{stats}");
230 assert!(output.contains("Execution Statistics:"));
231 }
232}