ringkernel_procint/models/
dfg.rs1use super::ActivityId;
4use rkyv::{Archive, Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Copy, Default, Archive, Serialize, Deserialize)]
9#[repr(C, align(64))]
10pub struct GpuDFGNode {
11 pub activity_id: u32,
13 pub event_count: u32,
15 pub total_duration_ms: u64,
17 pub min_duration_ms: u32,
19 pub max_duration_ms: u32,
21 pub avg_duration_ms: f32,
23 pub std_duration_ms: f32,
25 pub first_seen_ms: u64,
27 pub last_seen_ms: u64,
29 pub is_start: u8,
31 pub is_end: u8,
33 pub flags: u8,
35 pub _padding: u8,
37 pub incoming_count: u16,
39 pub outgoing_count: u16,
41}
42
43const _: () = assert!(std::mem::size_of::<GpuDFGNode>() == 64);
45
46#[derive(Debug, Clone, Copy, Default, Archive, Serialize, Deserialize)]
48#[repr(C, align(64))]
49pub struct GpuDFGEdge {
50 pub source_activity: u32,
52 pub target_activity: u32,
54 pub frequency: u32,
56 pub unique_cases: u32,
58 pub total_duration_ms: u64,
60 pub min_duration_ms: u32,
62 pub max_duration_ms: u32,
64 pub avg_duration_ms: f32,
66 pub std_duration_ms: f32,
68 pub probability: f32,
70 pub flags: u32,
72 pub _reserved: [u8; 16],
74}
75
76const _: () = assert!(std::mem::size_of::<GpuDFGEdge>() == 64);
78
79impl GpuDFGEdge {
80 pub fn is_self_loop(&self) -> bool {
82 self.source_activity == self.target_activity
83 }
84
85 pub fn is_loop(&self) -> bool {
87 (self.flags & 0x01) != 0
88 }
89
90 pub fn is_back_edge(&self) -> bool {
92 (self.flags & 0x02) != 0
93 }
94}
95
96#[derive(Debug, Clone, Copy, Archive, Serialize, Deserialize)]
98#[repr(C, align(128))]
99pub struct GpuDFGGraph {
100 pub node_count: u32,
102 pub edge_count: u32,
104 pub total_events: u64,
106 pub total_cases: u32,
108 pub unique_activities: u32,
110 pub avg_trace_length: f32,
112 pub max_trace_length: u32,
114 pub start_activity_count: u32,
116 pub end_activity_count: u32,
118 pub graph_density: f32,
120 pub loop_count: u32,
122 pub timestamp_min: u64,
124 pub timestamp_max: u64,
126 pub avg_activity_duration: f32,
128 pub _reserved: [u8; 56],
130}
131
132const _: () = assert!(std::mem::size_of::<GpuDFGGraph>() == 128);
134
135impl Default for GpuDFGGraph {
136 fn default() -> Self {
137 Self {
138 node_count: 0,
139 edge_count: 0,
140 total_events: 0,
141 total_cases: 0,
142 unique_activities: 0,
143 avg_trace_length: 0.0,
144 max_trace_length: 0,
145 start_activity_count: 0,
146 end_activity_count: 0,
147 graph_density: 0.0,
148 loop_count: 0,
149 timestamp_min: 0,
150 timestamp_max: 0,
151 avg_activity_duration: 0.0,
152 _reserved: [0; 56],
153 }
154 }
155}
156
157#[derive(Debug, Clone, Default)]
159pub struct DFGGraph {
160 pub header: GpuDFGGraph,
162 pub nodes: Vec<GpuDFGNode>,
164 pub edges: Vec<GpuDFGEdge>,
166 pub node_index: HashMap<ActivityId, usize>,
168 pub edge_index: HashMap<(ActivityId, ActivityId), usize>,
170}
171
172impl DFGGraph {
173 pub fn new() -> Self {
175 Self::default()
176 }
177
178 pub fn nodes(&self) -> &[GpuDFGNode] {
180 &self.nodes
181 }
182
183 pub fn edges(&self) -> &[GpuDFGEdge] {
185 &self.edges
186 }
187
188 pub fn node_count(&self) -> usize {
190 self.nodes.len()
191 }
192
193 pub fn edge_count(&self) -> usize {
195 self.edges.len()
196 }
197
198 pub fn from_gpu(nodes: Vec<GpuDFGNode>, edges: Vec<GpuDFGEdge>) -> Self {
200 let mut dfg = Self {
201 header: GpuDFGGraph {
202 node_count: nodes.len() as u32,
203 edge_count: edges.len() as u32,
204 ..Default::default()
205 },
206 nodes,
207 edges,
208 node_index: HashMap::new(),
209 edge_index: HashMap::new(),
210 };
211
212 for (i, node) in dfg.nodes.iter().enumerate() {
214 dfg.node_index.insert(node.activity_id, i);
215 }
216 for (i, edge) in dfg.edges.iter().enumerate() {
217 dfg.edge_index
218 .insert((edge.source_activity, edge.target_activity), i);
219 }
220
221 dfg
222 }
223
224 pub fn update_node(
226 &mut self,
227 activity_id: ActivityId,
228 event_count: u32,
229 avg_duration: f32,
230 _cost: f32,
231 ) {
232 self.add_or_update_node(activity_id, |node| {
233 node.event_count = event_count;
234 node.avg_duration_ms = avg_duration;
235 });
237 }
238
239 pub fn update_edge(
241 &mut self,
242 source: ActivityId,
243 target: ActivityId,
244 frequency: u32,
245 avg_duration: f32,
246 ) {
247 self.add_or_update_edge(source, target, |edge| {
248 edge.frequency = frequency;
249 edge.avg_duration_ms = avg_duration;
250 });
251 }
252
253 pub fn get_node(&self, activity_id: ActivityId) -> Option<&GpuDFGNode> {
255 self.node_index.get(&activity_id).map(|&i| &self.nodes[i])
256 }
257
258 pub fn get_node_mut(&mut self, activity_id: ActivityId) -> Option<&mut GpuDFGNode> {
260 self.node_index
261 .get(&activity_id)
262 .map(|&i| &mut self.nodes[i])
263 }
264
265 pub fn get_edge(&self, source: ActivityId, target: ActivityId) -> Option<&GpuDFGEdge> {
267 self.edge_index
268 .get(&(source, target))
269 .map(|&i| &self.edges[i])
270 }
271
272 pub fn add_or_update_node(
274 &mut self,
275 activity_id: ActivityId,
276 update_fn: impl FnOnce(&mut GpuDFGNode),
277 ) {
278 if let Some(&idx) = self.node_index.get(&activity_id) {
279 update_fn(&mut self.nodes[idx]);
280 } else {
281 let idx = self.nodes.len();
282 let mut node = GpuDFGNode {
283 activity_id,
284 ..Default::default()
285 };
286 update_fn(&mut node);
287 self.nodes.push(node);
288 self.node_index.insert(activity_id, idx);
289 self.header.node_count = self.nodes.len() as u32;
290 }
291 }
292
293 pub fn add_or_update_edge(
295 &mut self,
296 source: ActivityId,
297 target: ActivityId,
298 update_fn: impl FnOnce(&mut GpuDFGEdge),
299 ) {
300 let key = (source, target);
301 if let Some(&idx) = self.edge_index.get(&key) {
302 update_fn(&mut self.edges[idx]);
303 } else {
304 let idx = self.edges.len();
305 let mut edge = GpuDFGEdge {
306 source_activity: source,
307 target_activity: target,
308 ..Default::default()
309 };
310 update_fn(&mut edge);
311 self.edges.push(edge);
312 self.edge_index.insert(key, idx);
313 self.header.edge_count = self.edges.len() as u32;
314 }
315 }
316
317 pub fn start_activities(&self) -> impl Iterator<Item = &GpuDFGNode> {
319 self.nodes.iter().filter(|n| n.is_start != 0)
320 }
321
322 pub fn end_activities(&self) -> impl Iterator<Item = &GpuDFGNode> {
324 self.nodes.iter().filter(|n| n.is_end != 0)
325 }
326
327 pub fn outgoing_edges(&self, activity_id: ActivityId) -> impl Iterator<Item = &GpuDFGEdge> {
329 self.edges
330 .iter()
331 .filter(move |e| e.source_activity == activity_id)
332 }
333
334 pub fn incoming_edges(&self, activity_id: ActivityId) -> impl Iterator<Item = &GpuDFGEdge> {
336 self.edges
337 .iter()
338 .filter(move |e| e.target_activity == activity_id)
339 }
340
341 pub fn calculate_probabilities(&mut self) {
343 let mut outgoing_totals: HashMap<ActivityId, u32> = HashMap::new();
345 for edge in &self.edges {
346 *outgoing_totals.entry(edge.source_activity).or_insert(0) += edge.frequency;
347 }
348
349 for edge in &mut self.edges {
351 if let Some(&total) = outgoing_totals.get(&edge.source_activity) {
352 edge.probability = if total > 0 {
353 edge.frequency as f32 / total as f32
354 } else {
355 0.0
356 };
357 }
358 }
359 }
360
361 pub fn update_degrees(&mut self) {
363 for node in &mut self.nodes {
365 node.incoming_count = 0;
366 node.outgoing_count = 0;
367 }
368
369 let mut incoming: HashMap<ActivityId, u16> = HashMap::new();
371 let mut outgoing: HashMap<ActivityId, u16> = HashMap::new();
372
373 for edge in &self.edges {
374 *outgoing.entry(edge.source_activity).or_insert(0) += 1;
375 *incoming.entry(edge.target_activity).or_insert(0) += 1;
376 }
377
378 for node in &mut self.nodes {
380 if let Some(&count) = outgoing.get(&node.activity_id) {
381 node.outgoing_count = count;
382 }
383 if let Some(&count) = incoming.get(&node.activity_id) {
384 node.incoming_count = count;
385 }
386 }
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_dfg_node_size() {
396 assert_eq!(std::mem::size_of::<GpuDFGNode>(), 64);
397 }
398
399 #[test]
400 fn test_dfg_edge_size() {
401 assert_eq!(std::mem::size_of::<GpuDFGEdge>(), 64);
402 }
403
404 #[test]
405 fn test_dfg_header_size() {
406 assert_eq!(std::mem::size_of::<GpuDFGGraph>(), 128);
407 }
408
409 #[test]
410 fn test_dfg_graph_operations() {
411 let mut dfg = DFGGraph::new();
412
413 dfg.add_or_update_node(1, |n| {
414 n.event_count = 10;
415 n.is_start = 1;
416 });
417
418 dfg.add_or_update_node(2, |n| {
419 n.event_count = 8;
420 n.is_end = 1;
421 });
422
423 dfg.add_or_update_edge(1, 2, |e| {
424 e.frequency = 8;
425 });
426
427 assert_eq!(dfg.nodes.len(), 2);
428 assert_eq!(dfg.edges.len(), 1);
429
430 dfg.calculate_probabilities();
431 assert_eq!(dfg.edges[0].probability, 1.0);
432 }
433}