1use crate::{ComputationGraph, JitError, JitResult, Node, NodeId};
8use petgraph::graph::NodeIndex;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::sync::{
12 atomic::{AtomicBool, AtomicU64, Ordering},
13 Arc, RwLock,
14};
15
16pub struct SpeculativeOptimizer {
18 config: SpeculativeConfig,
19 assumptions: Arc<RwLock<HashMap<AssumptionId, Assumption>>>,
20 guards: Arc<RwLock<HashMap<NodeId, Vec<Guard>>>>,
21 deopt_counter: AtomicU64,
22 enabled: AtomicBool,
23}
24
25#[derive(Debug, Clone)]
27pub struct SpeculativeConfig {
28 pub max_assumptions: usize,
30
31 pub deopt_threshold: u64,
33
34 pub confidence_threshold: f64,
36
37 pub enable_type_speculation: bool,
39
40 pub enable_shape_speculation: bool,
42
43 pub enable_value_speculation: bool,
45
46 pub enable_nullability_speculation: bool,
48
49 pub enable_branch_speculation: bool,
51
52 pub enable_loop_speculation: bool,
54
55 pub aggressiveness: f64,
57}
58
59impl Default for SpeculativeConfig {
60 fn default() -> Self {
61 Self {
62 max_assumptions: 1000,
63 deopt_threshold: 100,
64 confidence_threshold: 0.8,
65 enable_type_speculation: true,
66 enable_shape_speculation: true,
67 enable_value_speculation: false, enable_nullability_speculation: true,
69 enable_branch_speculation: true,
70 enable_loop_speculation: true,
71 aggressiveness: 0.7,
72 }
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
78pub struct AssumptionId(pub u64);
79
80#[derive(Debug, Clone)]
82pub struct Assumption {
83 pub id: AssumptionId,
84 pub assumption_type: AssumptionType,
85 pub node_id: NodeId,
86 pub confidence: f64,
87 pub success_count: u64,
88 pub failure_count: u64,
89 pub created_at: std::time::SystemTime,
90 pub metadata: HashMap<String, String>,
91}
92
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub enum AssumptionType {
96 TypeSpeculation { expected_type: String },
98
99 ShapeSpeculation { expected_shape: Vec<usize> },
101
102 ValueSpeculation { expected_value: f64, tolerance: f64 },
104
105 NullabilitySpeculation,
107
108 BranchSpeculation {
110 usually_taken: bool,
111 probability: f64,
112 },
113
114 LoopSpeculation {
116 expected_iterations: u64,
117 tolerance: u64,
118 },
119
120 MemorySpeculation { access_pattern: MemoryAccessPattern },
122}
123
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub enum MemoryAccessPattern {
127 Sequential,
128 Random,
129 Strided { stride: usize },
130 Clustered { cluster_size: usize },
131}
132
133#[derive(Debug, Clone)]
135pub struct Guard {
136 pub assumption_id: AssumptionId,
137 pub guard_type: GuardType,
138 pub check_frequency: GuardFrequency,
139}
140
141#[derive(Debug, Clone, PartialEq)]
143pub enum GuardType {
144 TypeCheck,
146
147 ShapeCheck,
149
150 ValueCheck,
152
153 NullabilityCheck,
155
156 BranchCheck,
158
159 LoopCheck,
161
162 MemoryCheck,
164}
165
166#[derive(Debug, Clone, PartialEq)]
168pub enum GuardFrequency {
169 Always,
171
172 Probabilistic(f64),
174
175 Periodic(u64),
177
178 InitialOnly(u64),
180}
181
182#[derive(Debug, Clone)]
184pub struct SpeculationResult {
185 pub assumptions_made: Vec<AssumptionId>,
186 pub optimizations_applied: Vec<SpeculativeOptimization>,
187 pub guards_installed: Vec<Guard>,
188 pub estimated_speedup: f64,
189}
190
191#[derive(Debug, Clone)]
193pub struct SpeculativeOptimization {
194 pub optimization_type: SpeculativeOptimizationType,
195 pub node_id: NodeId,
196 pub description: String,
197 pub estimated_benefit: f64,
198}
199
200#[derive(Debug, Clone, PartialEq)]
202pub enum SpeculativeOptimizationType {
203 TypeCheckElimination,
205
206 BoundsCheckElimination,
208
209 ShapeSpecialization,
211
212 ConstantPropagation,
214
215 DeadCodeElimination,
217
218 LoopUnrolling,
220
221 BranchElimination,
223
224 MemoryPrefetching,
226
227 VectorizationOptimization,
229}
230
231#[derive(Debug, Clone)]
233pub struct DeoptimizationEvent {
234 pub assumption_id: AssumptionId,
235 pub node_id: NodeId,
236 pub reason: String,
237 pub timestamp: std::time::SystemTime,
238 pub execution_count: u64,
239}
240
241impl SpeculativeOptimizer {
242 pub fn new(config: SpeculativeConfig) -> Self {
244 Self {
245 config,
246 assumptions: Arc::new(RwLock::new(HashMap::new())),
247 guards: Arc::new(RwLock::new(HashMap::new())),
248 deopt_counter: AtomicU64::new(0),
249 enabled: AtomicBool::new(true),
250 }
251 }
252
253 pub fn analyze_and_speculate(
255 &self,
256 graph: &ComputationGraph,
257 execution_history: &ExecutionHistory,
258 ) -> JitResult<SpeculationResult> {
259 if !self.enabled.load(Ordering::Relaxed) {
260 return Ok(SpeculationResult {
261 assumptions_made: Vec::new(),
262 optimizations_applied: Vec::new(),
263 guards_installed: Vec::new(),
264 estimated_speedup: 1.0,
265 });
266 }
267
268 let mut assumptions_made = Vec::new();
269 let mut optimizations = Vec::new();
270 let mut guards = Vec::new();
271 let mut total_speedup = 1.0;
272
273 for (node_id, node) in graph.nodes() {
274 if let Some(node_history) = execution_history.get_node_history(node_id) {
276 if self.config.enable_type_speculation {
278 if let Some(spec_result) =
279 self.analyze_type_speculation(node_id, node, node_history)?
280 {
281 assumptions_made.extend(spec_result.assumptions_made);
282 optimizations.extend(spec_result.optimizations_applied);
283 guards.extend(spec_result.guards_installed);
284 total_speedup *= spec_result.estimated_speedup;
285 }
286 }
287
288 if self.config.enable_shape_speculation {
290 if let Some(spec_result) =
291 self.analyze_shape_speculation(node_id, node, node_history)?
292 {
293 assumptions_made.extend(spec_result.assumptions_made);
294 optimizations.extend(spec_result.optimizations_applied);
295 guards.extend(spec_result.guards_installed);
296 total_speedup *= spec_result.estimated_speedup;
297 }
298 }
299
300 if self.config.enable_value_speculation {
302 if let Some(spec_result) =
303 self.analyze_value_speculation(node_id, node, node_history)?
304 {
305 assumptions_made.extend(spec_result.assumptions_made);
306 optimizations.extend(spec_result.optimizations_applied);
307 guards.extend(spec_result.guards_installed);
308 total_speedup *= spec_result.estimated_speedup;
309 }
310 }
311
312 if self.config.enable_branch_speculation {
314 if let Some(spec_result) =
315 self.analyze_branch_speculation(node_id, node, node_history)?
316 {
317 assumptions_made.extend(spec_result.assumptions_made);
318 optimizations.extend(spec_result.optimizations_applied);
319 guards.extend(spec_result.guards_installed);
320 total_speedup *= spec_result.estimated_speedup;
321 }
322 }
323 }
324 }
325
326 if let Ok(mut guard_map) = self.guards.write() {
328 for guard in &guards {
329 guard_map
330 .entry(NodeIndex::new(guard.assumption_id.0 as usize))
331 .or_insert_with(Vec::new)
332 .push(guard.clone());
333 }
334 }
335
336 if let Ok(mut assumption_map) = self.assumptions.write() {
340 for (assumption_id, optimization) in assumptions_made.iter().zip(optimizations.iter()) {
341 let assumption = self.build_assumption(*assumption_id, optimization);
342 assumption_map.insert(*assumption_id, assumption);
343 }
344 }
345
346 Ok(SpeculationResult {
347 assumptions_made,
348 optimizations_applied: optimizations,
349 guards_installed: guards,
350 estimated_speedup: total_speedup,
351 })
352 }
353
354 pub fn apply_speculative_optimizations(
356 &self,
357 graph: &mut ComputationGraph,
358 result: &SpeculationResult,
359 ) -> JitResult<usize> {
360 let mut applied_count = 0;
361
362 for optimization in &result.optimizations_applied {
363 match optimization.optimization_type {
364 SpeculativeOptimizationType::TypeCheckElimination => {
365 if self.apply_type_check_elimination(graph, optimization)? {
366 applied_count += 1;
367 }
368 }
369 SpeculativeOptimizationType::ShapeSpecialization => {
370 if self.apply_shape_specialization(graph, optimization)? {
371 applied_count += 1;
372 }
373 }
374 SpeculativeOptimizationType::ConstantPropagation => {
375 if self.apply_constant_propagation(graph, optimization)? {
376 applied_count += 1;
377 }
378 }
379 SpeculativeOptimizationType::BranchElimination => {
380 if self.apply_branch_elimination(graph, optimization)? {
381 applied_count += 1;
382 }
383 }
384 _ => {
385 }
387 }
388 }
389
390 Ok(applied_count)
391 }
392
393 pub fn check_guards(&self, node_id: NodeId, runtime_info: &RuntimeInfo) -> JitResult<bool> {
395 let guard_map = self
396 .guards
397 .read()
398 .map_err(|_| JitError::RuntimeError("Failed to read guards".to_string()))?;
399
400 if let Some(node_guards) = guard_map.get(&node_id) {
401 for guard in node_guards {
402 if self.should_check_guard(guard, runtime_info.execution_count) {
403 let check_passed = self.execute_guard_check(guard, runtime_info)?;
404
405 if !check_passed {
406 self.handle_deoptimization(
407 guard.assumption_id,
408 node_id,
409 "Guard check failed",
410 )?;
411 return Ok(false);
412 }
413 }
414 }
415 }
416
417 Ok(true)
418 }
419
420 pub fn record_success(&self, assumption_id: AssumptionId) {
422 if let Ok(mut assumptions) = self.assumptions.write() {
423 if let Some(assumption) = assumptions.get_mut(&assumption_id) {
424 assumption.success_count += 1;
425 assumption.confidence = self.calculate_confidence(assumption);
426 }
427 }
428 }
429
430 pub fn handle_deoptimization(
432 &self,
433 assumption_id: AssumptionId,
434 node_id: NodeId,
435 reason: &str,
436 ) -> JitResult<()> {
437 let deopt_count = self.deopt_counter.fetch_add(1, Ordering::Relaxed);
438
439 if let Ok(mut assumptions) = self.assumptions.write() {
441 if let Some(assumption) = assumptions.get_mut(&assumption_id) {
442 assumption.failure_count += 1;
443 assumption.confidence = self.calculate_confidence(assumption);
444
445 if assumption.confidence < 0.3 {
447 assumptions.remove(&assumption_id);
448 }
449 }
450 }
451
452 if deopt_count > self.config.deopt_threshold {
454 self.enabled.store(false, Ordering::Relaxed);
455 }
456
457 let event = DeoptimizationEvent {
459 assumption_id,
460 node_id,
461 reason: reason.to_string(),
462 timestamp: std::time::SystemTime::now(),
463 execution_count: deopt_count,
464 };
465
466 self.log_deoptimization_event(&event);
467
468 Ok(())
469 }
470
471 pub fn get_statistics(&self) -> JitResult<SpeculationStatistics> {
473 let assumptions = self
474 .assumptions
475 .read()
476 .map_err(|_| JitError::RuntimeError("Failed to read assumptions".to_string()))?;
477
478 let active_assumptions = assumptions.len();
479 let total_successes = assumptions.values().map(|a| a.success_count).sum();
480 let total_failures = assumptions.values().map(|a| a.failure_count).sum();
481 let avg_confidence = if !assumptions.is_empty() {
482 assumptions.values().map(|a| a.confidence).sum::<f64>() / assumptions.len() as f64
483 } else {
484 0.0
485 };
486
487 let deopt_count = self.deopt_counter.load(Ordering::Relaxed);
488 let enabled = self.enabled.load(Ordering::Relaxed);
489
490 Ok(SpeculationStatistics {
491 active_assumptions,
492 total_successes,
493 total_failures,
494 avg_confidence,
495 deoptimization_count: deopt_count,
496 enabled,
497 })
498 }
499
500 fn analyze_type_speculation(
502 &self,
503 node_id: NodeId,
504 _node: &Node,
505 history: &NodeExecutionHistory,
506 ) -> JitResult<Option<SpeculationResult>> {
507 if let Some(dominant_type) = history.get_dominant_type(self.config.confidence_threshold) {
509 let assumption_id = self.generate_assumption_id();
510
511 let optimization = SpeculativeOptimization {
512 optimization_type: SpeculativeOptimizationType::TypeCheckElimination,
513 node_id,
514 description: format!("Assume type is always {}", dominant_type),
515 estimated_benefit: 0.05, };
517
518 let guard = Guard {
519 assumption_id,
520 guard_type: GuardType::TypeCheck,
521 check_frequency: GuardFrequency::Probabilistic(0.1), };
523
524 return Ok(Some(SpeculationResult {
525 assumptions_made: vec![assumption_id],
526 optimizations_applied: vec![optimization],
527 guards_installed: vec![guard],
528 estimated_speedup: 1.05,
529 }));
530 }
531
532 Ok(None)
533 }
534
535 fn analyze_shape_speculation(
536 &self,
537 node_id: NodeId,
538 _node: &Node,
539 history: &NodeExecutionHistory,
540 ) -> JitResult<Option<SpeculationResult>> {
541 if let Some(dominant_shape) = history.get_dominant_shape(self.config.confidence_threshold) {
543 let assumption_id = self.generate_assumption_id();
544
545 let optimization = SpeculativeOptimization {
546 optimization_type: SpeculativeOptimizationType::ShapeSpecialization,
547 node_id,
548 description: format!("Specialize for shape {:?}", dominant_shape),
549 estimated_benefit: 0.15, };
551
552 let guard = Guard {
553 assumption_id,
554 guard_type: GuardType::ShapeCheck,
555 check_frequency: GuardFrequency::Always, };
557
558 return Ok(Some(SpeculationResult {
559 assumptions_made: vec![assumption_id],
560 optimizations_applied: vec![optimization],
561 guards_installed: vec![guard],
562 estimated_speedup: 1.15,
563 }));
564 }
565
566 Ok(None)
567 }
568
569 fn analyze_value_speculation(
570 &self,
571 node_id: NodeId,
572 _node: &Node,
573 history: &NodeExecutionHistory,
574 ) -> JitResult<Option<SpeculationResult>> {
575 if let Some(constant_value) = history.get_constant_value(self.config.confidence_threshold) {
577 let assumption_id = self.generate_assumption_id();
578
579 let optimization = SpeculativeOptimization {
580 optimization_type: SpeculativeOptimizationType::ConstantPropagation,
581 node_id,
582 description: format!("Assume constant value {}", constant_value),
583 estimated_benefit: 0.20, };
585
586 let guard = Guard {
587 assumption_id,
588 guard_type: GuardType::ValueCheck,
589 check_frequency: GuardFrequency::Periodic(100), };
591
592 return Ok(Some(SpeculationResult {
593 assumptions_made: vec![assumption_id],
594 optimizations_applied: vec![optimization],
595 guards_installed: vec![guard],
596 estimated_speedup: 1.20,
597 }));
598 }
599
600 Ok(None)
601 }
602
603 fn analyze_branch_speculation(
604 &self,
605 node_id: NodeId,
606 _node: &Node,
607 history: &NodeExecutionHistory,
608 ) -> JitResult<Option<SpeculationResult>> {
609 if let Some(branch_bias) = history.get_branch_bias(self.config.confidence_threshold) {
611 let assumption_id = self.generate_assumption_id();
612
613 let optimization = SpeculativeOptimization {
614 optimization_type: SpeculativeOptimizationType::BranchElimination,
615 node_id,
616 description: format!(
617 "Assume branch is usually {}",
618 if branch_bias > 0.5 {
619 "taken"
620 } else {
621 "not taken"
622 }
623 ),
624 estimated_benefit: 0.10, };
626
627 let guard = Guard {
628 assumption_id,
629 guard_type: GuardType::BranchCheck,
630 check_frequency: GuardFrequency::Probabilistic(0.05), };
632
633 return Ok(Some(SpeculationResult {
634 assumptions_made: vec![assumption_id],
635 optimizations_applied: vec![optimization],
636 guards_installed: vec![guard],
637 estimated_speedup: 1.10,
638 }));
639 }
640
641 Ok(None)
642 }
643
644 fn apply_type_check_elimination(
645 &self,
646 graph: &mut ComputationGraph,
647 optimization: &SpeculativeOptimization,
648 ) -> JitResult<bool> {
649 let node_id = optimization.node_id;
650
651 if let Some(node) = graph.node_mut(node_id) {
652 node.set_optimization_hint("eliminate_type_checks", "true")?;
654 node.set_optimization_hint("assumed_type_stable", "true")?;
655
656 node.set_optimization_hint("add_type_guard", "true")?;
658 node.set_optimization_hint("guard_frequency", "low")?;
659
660 return Ok(true);
661 }
662
663 Ok(false)
664 }
665
666 fn apply_shape_specialization(
667 &self,
668 graph: &mut ComputationGraph,
669 optimization: &SpeculativeOptimization,
670 ) -> JitResult<bool> {
671 let node_id = optimization.node_id;
672
673 if let Some(node) = graph.node_mut(node_id) {
674 node.set_optimization_hint("shape_specialized", "true")?;
676 node.set_optimization_hint("eliminate_shape_checks", "true")?;
677
678 if optimization.description.contains("shape") {
680 node.set_optimization_hint("specialized_shape_source", "speculation")?;
681 node.set_optimization_hint("add_shape_guard", "true")?;
682 }
683
684 return Ok(true);
685 }
686
687 Ok(false)
688 }
689
690 fn apply_constant_propagation(
691 &self,
692 graph: &mut ComputationGraph,
693 optimization: &SpeculativeOptimization,
694 ) -> JitResult<bool> {
695 let node_id = optimization.node_id;
696
697 if let Some(node) = graph.node_mut(node_id) {
698 node.set_optimization_hint("constant_propagation", "true")?;
700 node.set_optimization_hint("assumed_constant", "true")?;
701
702 if let Some(start) = optimization.description.find("value ") {
704 if let Some(end) = optimization.description[start + 6..].find(' ') {
705 let value_str = &optimization.description[start + 6..start + 6 + end];
706 node.set_optimization_hint("assumed_constant_value", value_str)?;
707 }
708 }
709
710 node.set_optimization_hint("add_value_guard", "true")?;
712 node.set_optimization_hint("guard_tolerance", "1e-10")?;
713
714 return Ok(true);
715 }
716
717 Ok(false)
718 }
719
720 fn apply_branch_elimination(
721 &self,
722 graph: &mut ComputationGraph,
723 optimization: &SpeculativeOptimization,
724 ) -> JitResult<bool> {
725 let node_id = optimization.node_id;
726
727 if let Some(node) = graph.node_mut(node_id) {
728 let usually_taken = optimization.description.contains("usually taken");
730
731 if usually_taken {
732 node.set_optimization_hint("branch_likely", "true")?;
733 node.set_optimization_hint("optimize_taken_path", "true")?;
734 } else {
735 node.set_optimization_hint("branch_unlikely", "true")?;
736 node.set_optimization_hint("optimize_not_taken_path", "true")?;
737 }
738
739 if optimization.estimated_benefit > 0.08 {
741 node.set_optimization_hint("branch_elimination_candidate", "true")?;
743 node.set_optimization_hint("speculative_branch_elimination", "true")?;
744 }
745
746 node.set_optimization_hint("add_branch_guard", "true")?;
748
749 return Ok(true);
750 }
751
752 Ok(false)
753 }
754
755 fn should_check_guard(&self, guard: &Guard, execution_count: u64) -> bool {
756 match guard.check_frequency {
757 GuardFrequency::Always => true,
758 GuardFrequency::Probabilistic(probability) => {
759 use std::collections::hash_map::DefaultHasher;
760 use std::hash::{Hash, Hasher};
761
762 let mut hasher = DefaultHasher::new();
763 execution_count.hash(&mut hasher);
764 let hash = hasher.finish();
765 (hash as f64 / u64::MAX as f64) < probability
766 }
767 GuardFrequency::Periodic(period) => execution_count % period == 0,
768 GuardFrequency::InitialOnly(limit) => execution_count < limit,
769 }
770 }
771
772 fn execute_guard_check(&self, guard: &Guard, runtime_info: &RuntimeInfo) -> JitResult<bool> {
773 match guard.guard_type {
774 GuardType::TypeCheck => {
775 Ok(runtime_info.actual_type == runtime_info.expected_type)
777 }
778 GuardType::ShapeCheck => {
779 Ok(runtime_info.actual_shape == runtime_info.expected_shape)
781 }
782 GuardType::ValueCheck => {
783 Ok(
785 (runtime_info.actual_value - runtime_info.expected_value).abs()
786 < runtime_info.tolerance,
787 )
788 }
789 GuardType::NullabilityCheck => {
790 Ok(!runtime_info.actual_value.is_nan() && runtime_info.actual_value.is_finite())
792 }
793 GuardType::BranchCheck => {
794 Ok(runtime_info.branch_taken == runtime_info.expected_branch_taken)
796 }
797 GuardType::LoopCheck => {
798 let diff = (runtime_info.actual_iterations as i64
800 - runtime_info.expected_iterations as i64)
801 .abs();
802 Ok(diff <= runtime_info.iteration_tolerance as i64)
803 }
804 GuardType::MemoryCheck => {
805 Ok(runtime_info.memory_pattern == runtime_info.expected_memory_pattern)
807 }
808 }
809 }
810
811 fn generate_assumption_id(&self) -> AssumptionId {
812 use std::sync::atomic::{AtomicU64, Ordering};
813 static COUNTER: AtomicU64 = AtomicU64::new(0);
814 AssumptionId(COUNTER.fetch_add(1, Ordering::Relaxed))
815 }
816
817 fn build_assumption(
821 &self,
822 id: AssumptionId,
823 optimization: &SpeculativeOptimization,
824 ) -> Assumption {
825 let assumption_type = match &optimization.optimization_type {
826 SpeculativeOptimizationType::TypeCheckElimination => {
827 let expected_type = optimization
830 .description
831 .strip_prefix("Assume type is always ")
832 .unwrap_or("unknown")
833 .to_string();
834 AssumptionType::TypeSpeculation { expected_type }
835 }
836 SpeculativeOptimizationType::ShapeSpecialization => {
837 let expected_shape = optimization
840 .description
841 .strip_prefix("Specialize for shape ")
842 .and_then(|s| {
843 let inner = s.trim_start_matches('[').trim_end_matches(']');
845 if inner.is_empty() {
846 Some(Vec::new())
847 } else {
848 inner
849 .split(", ")
850 .map(|tok| tok.trim().parse::<usize>().ok())
851 .collect::<Option<Vec<usize>>>()
852 }
853 })
854 .unwrap_or_default();
855 AssumptionType::ShapeSpeculation { expected_shape }
856 }
857 SpeculativeOptimizationType::ConstantPropagation => {
858 let (expected_value, tolerance) = optimization
860 .description
861 .strip_prefix("Assume constant value ")
862 .and_then(|s| s.split_whitespace().next())
863 .and_then(|tok| tok.parse::<f64>().ok())
864 .map(|v| (v, 1e-10))
865 .unwrap_or((0.0, 1e-10));
866 AssumptionType::ValueSpeculation {
867 expected_value,
868 tolerance,
869 }
870 }
871 SpeculativeOptimizationType::BranchElimination => {
872 let usually_taken = optimization.description.contains("usually taken");
873 let probability = (optimization.estimated_benefit * 10.0).clamp(0.5, 0.99);
875 AssumptionType::BranchSpeculation {
876 usually_taken,
877 probability,
878 }
879 }
880 _ => AssumptionType::NullabilitySpeculation,
883 };
884
885 let initial_confidence = self.config.confidence_threshold;
886 let mut metadata = HashMap::new();
887 metadata.insert(
888 "optimization_type".to_string(),
889 format!("{:?}", optimization.optimization_type),
890 );
891 metadata.insert(
892 "estimated_benefit".to_string(),
893 optimization.estimated_benefit.to_string(),
894 );
895
896 Assumption {
897 id,
898 assumption_type,
899 node_id: optimization.node_id,
900 confidence: initial_confidence,
901 success_count: 0,
902 failure_count: 0,
903 created_at: std::time::SystemTime::now(),
904 metadata,
905 }
906 }
907
908 fn calculate_confidence(&self, assumption: &Assumption) -> f64 {
909 let total = assumption.success_count + assumption.failure_count;
910 if total == 0 {
911 return 0.5; }
913
914 assumption.success_count as f64 / total as f64
915 }
916
917 fn log_deoptimization_event(&self, event: &DeoptimizationEvent) {
918 eprintln!("Deoptimization: {:?}", event);
920 }
921}
922
923#[derive(Debug, Clone)]
925pub struct RuntimeInfo {
926 pub execution_count: u64,
927 pub actual_type: String,
928 pub expected_type: String,
929 pub actual_shape: Vec<usize>,
930 pub expected_shape: Vec<usize>,
931 pub actual_value: f64,
932 pub expected_value: f64,
933 pub tolerance: f64,
934 pub branch_taken: bool,
935 pub expected_branch_taken: bool,
936 pub actual_iterations: u64,
937 pub expected_iterations: u64,
938 pub iteration_tolerance: u64,
939 pub memory_pattern: MemoryAccessPattern,
940 pub expected_memory_pattern: MemoryAccessPattern,
941}
942
943#[derive(Debug, Clone)]
945pub struct NodeExecutionHistory {
946 types: Vec<String>,
947 shapes: Vec<Vec<usize>>,
948 values: Vec<f64>,
949 branch_outcomes: Vec<bool>,
950 loop_iterations: Vec<u64>,
951}
952
953impl NodeExecutionHistory {
954 pub fn get_dominant_type(&self, threshold: f64) -> Option<String> {
955 let mut type_counts = HashMap::new();
956 for type_name in &self.types {
957 *type_counts.entry(type_name.clone()).or_insert(0) += 1;
958 }
959
960 if let Some((dominant_type, count)) = type_counts.iter().max_by_key(|(_, &count)| count) {
961 if *count as f64 / self.types.len() as f64 >= threshold {
962 return Some(dominant_type.clone());
963 }
964 }
965
966 None
967 }
968
969 pub fn get_dominant_shape(&self, threshold: f64) -> Option<Vec<usize>> {
970 let mut shape_counts = HashMap::new();
971 for shape in &self.shapes {
972 *shape_counts.entry(shape.clone()).or_insert(0) += 1;
973 }
974
975 if let Some((dominant_shape, count)) = shape_counts.iter().max_by_key(|(_, &count)| count) {
976 if *count as f64 / self.shapes.len() as f64 >= threshold {
977 return Some(dominant_shape.clone());
978 }
979 }
980
981 None
982 }
983
984 pub fn get_constant_value(&self, threshold: f64) -> Option<f64> {
985 if self.values.is_empty() {
986 return None;
987 }
988
989 let first_value = self.values[0];
991 let tolerance = 1e-10;
992 let constant_count = self
993 .values
994 .iter()
995 .filter(|&&v| (v - first_value).abs() < tolerance)
996 .count();
997
998 if constant_count as f64 / self.values.len() as f64 >= threshold {
999 Some(first_value)
1000 } else {
1001 None
1002 }
1003 }
1004
1005 pub fn get_branch_bias(&self, threshold: f64) -> Option<f64> {
1006 if self.branch_outcomes.is_empty() {
1007 return None;
1008 }
1009
1010 let taken_count = self.branch_outcomes.iter().filter(|&&taken| taken).count();
1011 let bias = taken_count as f64 / self.branch_outcomes.len() as f64;
1012
1013 if (bias - 0.5).abs() >= (threshold - 0.5) {
1015 Some(bias)
1016 } else {
1017 None
1018 }
1019 }
1020}
1021
1022#[derive(Debug, Clone)]
1024pub struct ExecutionHistory {
1025 node_histories: HashMap<NodeId, NodeExecutionHistory>,
1026}
1027
1028impl ExecutionHistory {
1029 pub fn new() -> Self {
1030 Self {
1031 node_histories: HashMap::new(),
1032 }
1033 }
1034
1035 pub fn get_node_history(&self, node_id: NodeId) -> Option<&NodeExecutionHistory> {
1036 self.node_histories.get(&node_id)
1037 }
1038
1039 pub fn record_execution(&mut self, node_id: NodeId, info: NodeExecutionInfo) {
1040 let history = self
1041 .node_histories
1042 .entry(node_id)
1043 .or_insert_with(|| NodeExecutionHistory {
1044 types: Vec::new(),
1045 shapes: Vec::new(),
1046 values: Vec::new(),
1047 branch_outcomes: Vec::new(),
1048 loop_iterations: Vec::new(),
1049 });
1050
1051 if let Some(type_name) = info.type_name {
1052 history.types.push(type_name);
1053 }
1054 if let Some(shape) = info.shape {
1055 history.shapes.push(shape);
1056 }
1057 if let Some(value) = info.value {
1058 history.values.push(value);
1059 }
1060 if let Some(branch_taken) = info.branch_taken {
1061 history.branch_outcomes.push(branch_taken);
1062 }
1063 if let Some(iterations) = info.loop_iterations {
1064 history.loop_iterations.push(iterations);
1065 }
1066 }
1067}
1068
1069#[derive(Debug, Clone)]
1071pub struct NodeExecutionInfo {
1072 pub type_name: Option<String>,
1073 pub shape: Option<Vec<usize>>,
1074 pub value: Option<f64>,
1075 pub branch_taken: Option<bool>,
1076 pub loop_iterations: Option<u64>,
1077}
1078
1079#[derive(Debug, Clone)]
1081pub struct SpeculationStatistics {
1082 pub active_assumptions: usize,
1083 pub total_successes: u64,
1084 pub total_failures: u64,
1085 pub avg_confidence: f64,
1086 pub deoptimization_count: u64,
1087 pub enabled: bool,
1088}
1089
1090#[cfg(test)]
1091mod tests {
1092 use super::*;
1093
1094 #[test]
1095 fn test_speculative_optimizer_creation() {
1096 let config = SpeculativeConfig::default();
1097 let optimizer = SpeculativeOptimizer::new(config);
1098 assert!(optimizer.enabled.load(Ordering::Relaxed));
1099 assert_eq!(optimizer.deopt_counter.load(Ordering::Relaxed), 0);
1100 }
1101
1102 #[test]
1103 fn test_assumption_id_generation() {
1104 let optimizer = SpeculativeOptimizer::new(SpeculativeConfig::default());
1105 let id1 = optimizer.generate_assumption_id();
1106 let id2 = optimizer.generate_assumption_id();
1107 assert_ne!(id1, id2);
1108 }
1109
1110 #[test]
1111 fn test_guard_frequency_checking() {
1112 let optimizer = SpeculativeOptimizer::new(SpeculativeConfig::default());
1113
1114 let always_guard = Guard {
1115 assumption_id: AssumptionId(1),
1116 guard_type: GuardType::TypeCheck,
1117 check_frequency: GuardFrequency::Always,
1118 };
1119 assert!(optimizer.should_check_guard(&always_guard, 100));
1120
1121 let periodic_guard = Guard {
1122 assumption_id: AssumptionId(2),
1123 guard_type: GuardType::TypeCheck,
1124 check_frequency: GuardFrequency::Periodic(10),
1125 };
1126 assert!(optimizer.should_check_guard(&periodic_guard, 100));
1127 assert!(!optimizer.should_check_guard(&periodic_guard, 101));
1128 }
1129
1130 #[test]
1131 fn test_execution_history() {
1132 let mut history = ExecutionHistory::new();
1133 let node_id = NodeId::new(1);
1134
1135 history.record_execution(
1137 node_id,
1138 NodeExecutionInfo {
1139 type_name: Some("f32".to_string()),
1140 shape: Some(vec![10, 20]),
1141 value: Some(1.0),
1142 branch_taken: Some(true),
1143 loop_iterations: Some(5),
1144 },
1145 );
1146
1147 history.record_execution(
1148 node_id,
1149 NodeExecutionInfo {
1150 type_name: Some("f32".to_string()),
1151 shape: Some(vec![10, 20]),
1152 value: Some(1.0),
1153 branch_taken: Some(true),
1154 loop_iterations: Some(5),
1155 },
1156 );
1157
1158 let node_history = history.get_node_history(node_id).unwrap();
1159 assert_eq!(node_history.get_dominant_type(0.8), Some("f32".to_string()));
1160 assert_eq!(node_history.get_dominant_shape(0.8), Some(vec![10, 20]));
1161 assert_eq!(node_history.get_constant_value(0.8), Some(1.0));
1162 assert_eq!(node_history.get_branch_bias(0.8), Some(1.0));
1163 }
1164}