ringkernel_procint/kernels/
pattern_detection.rs1use crate::cuda::{
6 generate_pattern_kernel, CpuFallbackExecutor, ExecutionResult, GpuStats, GpuStatus,
7 KernelExecutor,
8};
9use crate::models::{GpuDFGNode, GpuPatternMatch, PatternSeverity, PatternType};
10
11#[derive(Debug, Clone)]
13pub struct PatternConfig {
14 pub bottleneck_threshold: f32,
16 pub duration_threshold: f32,
18 pub min_confidence: f32,
20 pub max_patterns: usize,
22}
23
24impl Default for PatternConfig {
25 fn default() -> Self {
26 Self {
27 bottleneck_threshold: 2.0,
28 duration_threshold: 60000.0, min_confidence: 0.5,
30 max_patterns: 100,
31 }
32 }
33}
34
35pub struct PatternDetectionKernel {
37 config: PatternConfig,
39 executor: KernelExecutor,
41 use_gpu: bool,
43 kernel_compiled: bool,
45}
46
47impl Default for PatternDetectionKernel {
48 fn default() -> Self {
49 Self::new(PatternConfig::default())
50 }
51}
52
53impl PatternDetectionKernel {
54 pub fn new(config: PatternConfig) -> Self {
56 let mut kernel = Self {
57 config,
58 executor: KernelExecutor::new(),
59 use_gpu: true,
60 kernel_compiled: false,
61 };
62
63 kernel.try_compile_kernel();
65 kernel
66 }
67
68 fn try_compile_kernel(&mut self) {
70 if self.executor.is_cuda_available() && !self.kernel_compiled {
71 let source = generate_pattern_kernel();
72 match self.executor.compile(&source) {
73 Ok(_) => {
74 log::info!("Pattern detection CUDA kernel compiled successfully");
75 self.kernel_compiled = true;
76 }
77 Err(e) => {
78 log::warn!("Pattern detection CUDA kernel compilation failed: {}", e);
79 self.kernel_compiled = false;
80 }
81 }
82 }
83 }
84
85 pub fn with_cpu_only(mut self) -> Self {
87 self.use_gpu = false;
88 self
89 }
90
91 pub fn gpu_status(&self) -> GpuStatus {
93 self.executor.gpu_status()
94 }
95
96 pub fn gpu_stats(&self) -> &GpuStats {
98 &self.executor.stats
99 }
100
101 pub fn is_using_gpu(&self) -> bool {
103 self.use_gpu && self.kernel_compiled && self.executor.is_cuda_available()
104 }
105
106 pub fn detect(&mut self, nodes: &[GpuDFGNode]) -> PatternResult {
108 let start = std::time::Instant::now();
109
110 #[cfg(feature = "cuda")]
112 let (gpu_patterns, exec_result) = if self.is_using_gpu() {
113 match self.executor.execute_pattern_gpu(
114 nodes,
115 self.config.bottleneck_threshold,
116 self.config.duration_threshold,
117 ) {
118 Ok((patterns, result)) => {
119 log::debug!(
120 "Pattern GPU execution: {} nodes -> {} patterns in {}µs",
121 nodes.len(),
122 patterns.len(),
123 result.execution_time_us
124 );
125 (Some(patterns), result)
126 }
127 Err(e) => {
128 log::warn!("Pattern GPU execution failed, falling back to CPU: {}", e);
129 (None, ExecutionResult::default())
130 }
131 }
132 } else {
133 (None, ExecutionResult::default())
134 };
135
136 #[cfg(not(feature = "cuda"))]
137 let gpu_patterns: Option<Vec<GpuPatternMatch>> = None;
138 #[cfg(not(feature = "cuda"))]
139 let exec_result = ExecutionResult::default();
140
141 let (mut patterns, exec_result) = if let Some(gpu_patterns) = gpu_patterns {
143 (gpu_patterns, exec_result)
144 } else {
145 let mut patterns = Vec::with_capacity(self.config.max_patterns);
146 let result = CpuFallbackExecutor::execute_pattern_detection(
147 nodes,
148 &mut patterns,
149 self.config.bottleneck_threshold,
150 self.config.duration_threshold,
151 );
152 (patterns, result)
153 };
154
155 self.detect_loop_patterns(nodes, &mut patterns);
157
158 patterns.retain(|p| p.confidence >= self.config.min_confidence);
160
161 patterns.truncate(self.config.max_patterns);
163
164 let total_time = start.elapsed().as_micros() as u64;
165
166 PatternResult {
167 patterns,
168 execution_result: exec_result,
169 total_time_us: total_time,
170 }
171 }
172
173 fn detect_loop_patterns(&self, nodes: &[GpuDFGNode], patterns: &mut Vec<GpuPatternMatch>) {
175 for node in nodes {
176 if node.incoming_count > 0 && node.outgoing_count > 0 {
178 let in_count = node.incoming_count as f32;
180 let out_count = node.outgoing_count as f32;
181 let degree_ratio = in_count.min(out_count) / in_count.max(out_count).max(1.0);
182 if degree_ratio > 0.3 && node.event_count > 10 {
183 let mut pattern =
184 GpuPatternMatch::new(PatternType::Loop, PatternSeverity::Warning);
185 pattern.add_activity(node.activity_id);
186 pattern.confidence = degree_ratio;
187 pattern.frequency = node.event_count;
188 pattern.avg_duration_ms = node.avg_duration_ms;
189 patterns.push(pattern);
190 }
191 }
192 }
193 }
194}
195
196#[derive(Debug)]
198pub struct PatternResult {
199 pub patterns: Vec<GpuPatternMatch>,
201 pub execution_result: ExecutionResult,
203 pub total_time_us: u64,
205}
206
207impl PatternResult {
208 pub fn by_type(&self, pattern_type: PatternType) -> Vec<&GpuPatternMatch> {
210 self.patterns
211 .iter()
212 .filter(|p| p.get_pattern_type() == pattern_type)
213 .collect()
214 }
215
216 pub fn by_severity(&self, severity: PatternSeverity) -> Vec<&GpuPatternMatch> {
218 self.patterns
219 .iter()
220 .filter(|p| p.get_severity() == severity)
221 .collect()
222 }
223
224 pub fn count_by_type(&self) -> std::collections::HashMap<PatternType, usize> {
226 let mut counts = std::collections::HashMap::new();
227 for pattern in &self.patterns {
228 *counts.entry(pattern.get_pattern_type()).or_insert(0) += 1;
229 }
230 counts
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 fn create_test_nodes() -> Vec<GpuDFGNode> {
239 vec![
240 GpuDFGNode {
241 activity_id: 1,
242 event_count: 100,
243 avg_duration_ms: 5000.0,
244 incoming_count: 5, outgoing_count: 1, ..Default::default()
247 },
248 GpuDFGNode {
249 activity_id: 2,
250 event_count: 50,
251 avg_duration_ms: 120000.0, incoming_count: 2,
253 outgoing_count: 2,
254 ..Default::default()
255 },
256 ]
257 }
258
259 #[test]
260 fn test_pattern_detection() {
261 let mut kernel = PatternDetectionKernel::default().with_cpu_only();
262 let nodes = create_test_nodes();
263 let result = kernel.detect(&nodes);
264
265 assert!(!result.patterns.is_empty()); }
268
269 #[test]
270 fn test_pattern_filtering() {
271 let mut kernel = PatternDetectionKernel::default().with_cpu_only();
272 let nodes = create_test_nodes();
273 let result = kernel.detect(&nodes);
274
275 let long_running = result.by_type(PatternType::LongRunning);
276 assert!(!long_running.is_empty());
277 }
278}