1use runmat_time::Instant;
7use std::collections::{hash_map::Entry, HashMap};
8use std::path::Path;
9use std::sync::Arc;
10use std::time::Duration;
11
12use anyhow::{Context, Result};
13use indicatif::{ProgressBar, ProgressStyle};
14use parking_lot::RwLock;
15
16use crate::compression::{CompressionConfig, CompressionEngine};
17use crate::format::*;
18use crate::validation::SnapshotValidator;
19use crate::*;
20use runmat_hir::LoweringContext;
21
22pub struct SnapshotBuilder {
24 config: SnapshotConfig,
26
27 compression: CompressionEngine,
29
30 #[cfg(feature = "validation")]
32 validator: SnapshotValidator,
33
34 stats: Arc<RwLock<BuildStats>>,
36
37 progress: Option<ProgressBar>,
39}
40
41#[derive(Debug, Default)]
43pub struct BuildStats {
44 pub start_time: Option<Instant>,
46
47 pub phase_times: HashMap<String, Duration>,
49
50 pub memory_usage: Vec<(String, usize)>,
52
53 pub items_processed: HashMap<String, usize>,
55
56 pub errors: Vec<String>,
58
59 pub warnings: Vec<String>,
61}
62
63#[derive(Debug, Clone)]
65pub enum BuildPhase {
66 Initialization,
67 BuiltinRegistration,
68 HirCaching,
69 BytecodeCaching,
70 GcPresetCaching,
71 OptimizationAnalysis,
72 Compression,
73 Validation,
74 Serialization,
75 Finalization,
76}
77
78impl BuildPhase {
79 fn name(&self) -> &'static str {
80 match self {
81 BuildPhase::Initialization => "Initialization",
82 BuildPhase::BuiltinRegistration => "Builtin Registration",
83 BuildPhase::HirCaching => "HIR Caching",
84 BuildPhase::BytecodeCaching => "Bytecode Caching",
85 BuildPhase::GcPresetCaching => "GC Preset Caching",
86 BuildPhase::OptimizationAnalysis => "Optimization Analysis",
87 BuildPhase::Compression => "Compression",
88 BuildPhase::Validation => "Validation",
89 BuildPhase::Serialization => "Serialization",
90 BuildPhase::Finalization => "Finalization",
91 }
92 }
93
94 fn weight(&self) -> u64 {
95 match self {
96 BuildPhase::Initialization => 5,
97 BuildPhase::BuiltinRegistration => 15,
98 BuildPhase::HirCaching => 20,
99 BuildPhase::BytecodeCaching => 25,
100 BuildPhase::GcPresetCaching => 5,
101 BuildPhase::OptimizationAnalysis => 10,
102 BuildPhase::Compression => 10,
103 BuildPhase::Validation => 5,
104 BuildPhase::Serialization => 3,
105 BuildPhase::Finalization => 2,
106 }
107 }
108
109 pub fn needs_compression(&self) -> bool {
111 matches!(self, BuildPhase::Compression)
112 }
113
114 pub fn needs_validation(&self) -> bool {
116 matches!(self, BuildPhase::Validation)
117 }
118
119 pub fn involves_serialization(&self) -> bool {
121 matches!(self, BuildPhase::Serialization | BuildPhase::Finalization)
122 }
123}
124
125impl SnapshotBuilder {
126 pub fn new(config: SnapshotConfig) -> Self {
128 let compression_config = Self::compression_config_for(&config);
129
130 let compression = CompressionEngine::new(compression_config);
131
132 #[cfg(feature = "validation")]
133 let validator = SnapshotValidator::new();
134
135 let progress = if config.progress_reporting {
136 let pb = ProgressBar::new(100);
137 pb.set_style(
138 ProgressStyle::default_bar()
139 .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>3}/{len:3} {msg}")
140 .unwrap()
141 .progress_chars("#>-"),
142 );
143 Some(pb)
144 } else {
145 None
146 };
147
148 Self {
149 config,
150 compression,
151 #[cfg(feature = "validation")]
152 validator,
153 stats: Arc::new(RwLock::new(BuildStats::default())),
154 progress,
155 }
156 }
157
158 fn compression_config_for(config: &SnapshotConfig) -> CompressionConfig {
159 CompressionConfig {
160 default_level: config.compression_level,
161 adaptive_selection: matches!(
162 config.compression_algorithm,
163 crate::CompressionAlgorithm::Auto
164 ),
165 prefer_speed: matches!(
166 config.compression_algorithm,
167 crate::CompressionAlgorithm::Lz4
168 ) || config.compression_level <= 3,
169 ..CompressionConfig::default()
170 }
171 }
172
173 pub fn build_and_save<P: AsRef<Path>>(&self, output_path: P) -> SnapshotResult<()> {
175 let snapshot = self.build()?;
176 self.save_snapshot(&snapshot, output_path)
177 }
178
179 pub fn compression_engine(&self) -> &CompressionEngine {
181 &self.compression
182 }
183
184 #[cfg(feature = "validation")]
186 pub fn validator(&self) -> &SnapshotValidator {
187 &self.validator
188 }
189
190 #[cfg(test)]
192 pub fn test_all_phases() -> Vec<BuildPhase> {
193 vec![
194 BuildPhase::Initialization,
195 BuildPhase::BuiltinRegistration,
196 BuildPhase::HirCaching,
197 BuildPhase::BytecodeCaching,
198 BuildPhase::GcPresetCaching,
199 BuildPhase::OptimizationAnalysis,
200 BuildPhase::Compression,
201 BuildPhase::Validation,
202 BuildPhase::Serialization,
203 BuildPhase::Finalization,
204 ]
205 }
206
207 pub fn analyze_phase_requirements(phase: &BuildPhase) -> String {
209 let mut requirements = Vec::new();
210
211 if phase.needs_compression() {
212 requirements.push("compression engine");
213 }
214 if phase.needs_validation() {
215 requirements.push("validation framework");
216 }
217 if phase.involves_serialization() {
218 requirements.push("serialization support");
219 }
220
221 if requirements.is_empty() {
222 "No special requirements".to_string()
223 } else {
224 format!("Requires: {}", requirements.join(", "))
225 }
226 }
227
228 pub fn build(&self) -> SnapshotResult<Snapshot> {
230 self.start_build();
231
232 let phases = [
233 BuildPhase::Initialization,
234 BuildPhase::BuiltinRegistration,
235 BuildPhase::HirCaching,
236 BuildPhase::BytecodeCaching,
237 BuildPhase::GcPresetCaching,
238 BuildPhase::OptimizationAnalysis,
239 BuildPhase::Finalization,
240 ];
241
242 let mut current_progress = 0u64;
243 let total_progress: u64 = phases.iter().map(|p| p.weight()).sum();
244
245 let mut snapshot = self.execute_phase(BuildPhase::Initialization, || {
247 Ok(self.create_empty_snapshot())
248 })?;
249 current_progress += BuildPhase::Initialization.weight();
250 self.update_progress(
251 current_progress,
252 total_progress,
253 BuildPhase::Initialization.name(),
254 );
255
256 snapshot.builtins = self.execute_phase(BuildPhase::BuiltinRegistration, || {
258 self.build_builtin_registry()
259 })?;
260 current_progress += BuildPhase::BuiltinRegistration.weight();
261 self.update_progress(
262 current_progress,
263 total_progress,
264 BuildPhase::BuiltinRegistration.name(),
265 );
266
267 snapshot.hir_cache =
269 self.execute_phase(BuildPhase::HirCaching, || self.build_hir_cache())?;
270 current_progress += BuildPhase::HirCaching.weight();
271 self.update_progress(
272 current_progress,
273 total_progress,
274 BuildPhase::HirCaching.name(),
275 );
276
277 snapshot.bytecode_cache = self.execute_phase(BuildPhase::BytecodeCaching, || {
279 self.build_bytecode_cache(&snapshot.hir_cache)
280 })?;
281 current_progress += BuildPhase::BytecodeCaching.weight();
282 self.update_progress(
283 current_progress,
284 total_progress,
285 BuildPhase::BytecodeCaching.name(),
286 );
287
288 snapshot.gc_presets =
290 self.execute_phase(BuildPhase::GcPresetCaching, || self.build_gc_presets())?;
291 current_progress += BuildPhase::GcPresetCaching.weight();
292 self.update_progress(
293 current_progress,
294 total_progress,
295 BuildPhase::GcPresetCaching.name(),
296 );
297
298 snapshot.optimization_hints = self
300 .execute_phase(BuildPhase::OptimizationAnalysis, || {
301 self.generate_optimization_hints(&snapshot)
302 })?;
303 current_progress += BuildPhase::OptimizationAnalysis.weight();
304 self.update_progress(
305 current_progress,
306 total_progress,
307 BuildPhase::OptimizationAnalysis.name(),
308 );
309
310 self.execute_phase(BuildPhase::Finalization, || {
312 self.finalize_snapshot(&mut snapshot)
313 })?;
314 current_progress += BuildPhase::Finalization.weight();
315 self.update_progress(
316 current_progress,
317 total_progress,
318 BuildPhase::Finalization.name(),
319 );
320
321 self.finish_build();
322
323 Ok(snapshot)
324 }
325
326 fn execute_phase<T, F>(&self, phase: BuildPhase, f: F) -> SnapshotResult<T>
328 where
329 F: FnOnce() -> SnapshotResult<T>,
330 {
331 let start = Instant::now();
332 log::info!("Starting build phase: {}", phase.name());
333
334 let result = f().context(format!("Failed in phase: {}", phase.name()));
335
336 let duration = start.elapsed();
337 {
338 let mut stats = self.stats.write();
339 stats.phase_times.insert(phase.name().to_string(), duration);
340
341 match &result {
342 Ok(_) => {
343 log::info!("Completed build phase: {} in {:?}", phase.name(), duration);
344 }
345 Err(e) => {
346 let error_msg = format!("Failed in phase {}: {}", phase.name(), e);
347 log::error!("{error_msg}");
348 stats.errors.push(error_msg);
349 }
350 }
351 }
352
353 result.map_err(|e| SnapshotError::Configuration {
354 message: e.to_string(),
355 })
356 }
357
358 fn create_empty_snapshot(&self) -> Snapshot {
360 Snapshot {
361 metadata: SnapshotMetadata::current(),
362 builtins: BuiltinRegistry {
363 name_index: HashMap::new(),
364 functions: Vec::new(),
365 dispatch_table: Arc::new(RwLock::new(Vec::new())),
366 },
367 hir_cache: HirCache {
368 functions: HashMap::new(),
369 patterns: Vec::new(),
370 type_cache: HashMap::new(),
371 },
372 bytecode_cache: BytecodeCache {
373 stdlib_bytecode: HashMap::new(),
374 operation_sequences: Vec::new(),
375 hotspots: Vec::new(),
376 },
377 gc_presets: GcPresetCache {
378 presets: HashMap::new(),
379 default_preset: "default".to_string(),
380 performance_profiles: HashMap::new(),
381 },
382 optimization_hints: OptimizationHints {
383 jit_hints: Vec::new(),
384 memory_hints: Vec::new(),
385 execution_hints: Vec::new(),
386 },
387 }
388 }
389
390 fn build_builtin_registry(&self) -> SnapshotResult<BuiltinRegistry> {
392 log::info!("Building builtin function registry");
393
394 let builtins = runmat_builtins::builtin_functions();
395 let mut name_index = HashMap::new();
396 let mut functions = Vec::new();
397 let mut dispatch_table = Vec::new();
398
399 for (index, builtin) in builtins.iter().enumerate() {
400 match name_index.entry(builtin.name.to_string()) {
401 Entry::Vacant(slot) => {
402 slot.insert(index);
403 }
404 Entry::Occupied(existing) => {
405 log::warn!(
406 "Duplicate builtin '{}' detected while building snapshot (first index {}, duplicate index {})",
407 builtin.name,
408 existing.get(),
409 index
410 );
411 }
412 }
413
414 let metadata = self.analyze_builtin_function(builtin)?;
416 functions.push(metadata);
417
418 dispatch_table.push(builtin.implementation);
419
420 {
421 let mut stats = self.stats.write();
422 *stats
423 .items_processed
424 .entry("builtins".to_string())
425 .or_insert(0) += 1;
426 }
427 }
428
429 log::info!("Registered {} builtin functions", functions.len());
430
431 Ok(BuiltinRegistry {
432 name_index,
433 functions,
434 dispatch_table: Arc::new(RwLock::new(dispatch_table)),
435 })
436 }
437
438 fn analyze_builtin_function(
440 &self,
441 builtin: &runmat_builtins::BuiltinFunction,
442 ) -> SnapshotResult<BuiltinMetadata> {
443 let category = self.infer_builtin_category(builtin.name);
445 let complexity = self.infer_computational_complexity(builtin.name);
446 let optimization_level = self.infer_optimization_level(builtin.name, &category);
447
448 let arity = if builtin.name.ends_with("mul") || builtin.name.contains("dot") {
451 BuiltinArity::Exact(2)
452 } else if builtin.name == "norm"
453 || builtin.name.starts_with("sin")
454 || builtin.name.starts_with("cos")
455 {
456 BuiltinArity::Exact(1)
457 } else {
458 BuiltinArity::Range(1, 3)
459 };
460
461 Ok(BuiltinMetadata {
462 name: builtin.name.to_string(),
463 arity,
464 category,
465 complexity,
466 optimization_level,
467 })
468 }
469
470 fn infer_builtin_category(&self, name: &str) -> BuiltinCategory {
472 if name.contains("sin")
473 || name.contains("cos")
474 || name.contains("tan")
475 || name.contains("atan")
476 || name.contains("asin")
477 || name.contains("acos")
478 {
479 BuiltinCategory::Trigonometric
480 } else if name.contains("mat")
481 || name.contains("dot")
482 || name.contains("norm")
483 || name.contains("inv")
484 || name.contains("det")
485 {
486 BuiltinCategory::LinearAlgebra
487 } else if name.contains("mean") || name.contains("std") || name.contains("var") {
488 BuiltinCategory::Statistics
489 } else if name.contains("transpose") || name.contains("reshape") || name.contains("size") {
490 BuiltinCategory::MatrixOps
491 } else if name == "max"
492 || name == "min"
493 || name.contains("equal")
494 || name.contains("greater")
495 || name.contains("less")
496 {
497 BuiltinCategory::Comparison
498 } else if name.contains("sqrt")
499 || name.contains("exp")
500 || name.contains("log")
501 || name.contains("abs")
502 || name.contains("pow")
503 {
504 BuiltinCategory::Math
505 } else {
506 BuiltinCategory::Utility
507 }
508 }
509
510 fn infer_computational_complexity(&self, name: &str) -> ComputationalComplexity {
512 if name.contains("matmul") || name.contains("inv") || name.contains("det") {
513 ComputationalComplexity::Cubic
514 } else if name.contains("mat") && !name.contains("matmul") {
515 ComputationalComplexity::Quadratic
516 } else if name.contains("dot") || name.contains("norm") || name.contains("sum") {
517 ComputationalComplexity::Linear
518 } else {
519 ComputationalComplexity::Constant
520 }
521 }
522
523 fn infer_optimization_level(
525 &self,
526 _name: &str,
527 category: &BuiltinCategory,
528 ) -> OptimizationLevel {
529 let inferred = match category {
530 BuiltinCategory::LinearAlgebra | BuiltinCategory::MatrixOps => {
531 OptimizationLevel::MaxPerformance
532 }
533 BuiltinCategory::Math | BuiltinCategory::Trigonometric => OptimizationLevel::Aggressive,
534 BuiltinCategory::Statistics => OptimizationLevel::Basic,
535 _ => OptimizationLevel::None,
536 };
537
538 cap_optimization_level(inferred, self.config.max_optimization_level)
539 }
540
541 fn build_hir_cache(&self) -> SnapshotResult<HirCache> {
543 log::info!("Building HIR cache");
544
545 let mut functions = HashMap::new();
546 let mut patterns = Vec::new();
547 let mut type_cache = HashMap::new();
548
549 let stdlib_functions = self.get_stdlib_function_sources();
551
552 for (name, source) in stdlib_functions {
553 match self.compile_to_hir(&source) {
554 Ok(hir) => {
555 self.extract_type_info(&hir, &mut type_cache);
557
558 functions.insert(name.clone(), hir);
560
561 {
562 let mut stats = self.stats.write();
563 *stats
564 .items_processed
565 .entry("hir_functions".to_string())
566 .or_insert(0) += 1;
567 }
568 }
569 Err(e) => {
570 let warning = format!("Failed to compile {name} to HIR: {e}");
571 log::warn!("{warning}");
572
573 let mut stats = self.stats.write();
574 stats.warnings.push(warning);
575 }
576 }
577 }
578
579 patterns.extend(self.generate_common_patterns());
581
582 log::info!(
583 "Cached {} HIR functions and {} patterns",
584 functions.len(),
585 patterns.len()
586 );
587
588 Ok(HirCache {
589 functions,
590 patterns,
591 type_cache,
592 })
593 }
594
595 fn get_stdlib_function_sources(&self) -> Vec<(String, String)> {
597 vec![
598 (
599 "zeros".to_string(),
600 "function z = zeros(m, n); z = zeros(m, n); end".to_string(),
601 ),
602 (
603 "ones".to_string(),
604 "function o = ones(m, n); o = ones(m, n); end".to_string(),
605 ),
606 (
607 "eye".to_string(),
608 "function i = eye(n); i = eye(n); end".to_string(),
609 ),
610 (
611 "sum_vec".to_string(),
612 "function s = sum_vec(v); s = 0; for i = 1:length(v); s = s + v(i); end; end"
613 .to_string(),
614 ),
615 (
616 "mean_vec".to_string(),
617 "function m = mean_vec(v); m = sum_vec(v) / length(v); end".to_string(),
618 ),
619 ]
620 }
621
622 fn compile_to_hir(&self, source: &str) -> Result<runmat_hir::HirAssembly> {
624 let ast = runmat_parser::parse(source).map_err(|e| anyhow::anyhow!(e))?;
625 let hir =
626 runmat_hir::lower(&ast, &LoweringContext::empty()).map_err(|e| anyhow::anyhow!(e))?;
627 Ok(hir.assembly)
628 }
629
630 fn extract_type_info(
632 &self,
633 _hir: &runmat_hir::HirAssembly,
634 _type_cache: &mut HashMap<String, runmat_hir::Type>,
635 ) {
636 }
639
640 fn generate_common_patterns(&self) -> Vec<HirPattern> {
642 vec![
643 HirPattern {
645 name: "simple_for_loop".to_string(),
646 pattern: self.create_pattern_hir("for i = 1:n; x = x + 1; end"),
647 frequency: 1000,
648 optimization_priority: OptimizationLevel::Aggressive,
649 },
650 HirPattern {
652 name: "matrix_multiply".to_string(),
653 pattern: self.create_pattern_hir("C = A * B"),
654 frequency: 500,
655 optimization_priority: OptimizationLevel::MaxPerformance,
656 },
657 ]
658 }
659
660 fn create_pattern_hir(&self, source: &str) -> runmat_hir::HirAssembly {
662 self.compile_to_hir(source).unwrap_or_else(|_| {
663 runmat_hir::HirAssembly::default()
665 })
666 }
667
668 fn build_bytecode_cache(&self, hir_cache: &HirCache) -> SnapshotResult<BytecodeCache> {
670 log::info!("Building bytecode cache");
671
672 let mut stdlib_bytecode = HashMap::new();
673 let mut operation_sequences = Vec::new();
674 let mut hotspots = Vec::new();
675 let stdlib_sources: HashMap<_, _> =
676 self.get_stdlib_function_sources().into_iter().collect();
677
678 for (name, hir) in &hir_cache.functions {
680 let compiled = stdlib_sources
681 .get(name)
682 .map(|source| self.compile_source_to_bytecode(source))
683 .unwrap_or_else(|| self.compile_assembly_to_bytecode(hir));
684 match compiled {
685 Ok(bytecode) => {
686 stdlib_bytecode.insert(name.clone(), bytecode);
687
688 {
689 let mut stats = self.stats.write();
690 *stats
691 .items_processed
692 .entry("bytecode_functions".to_string())
693 .or_insert(0) += 1;
694 }
695 }
696 Err(e) => {
697 let warning = format!("Failed to compile {name} to bytecode: {e}");
698 log::warn!("{warning}");
699
700 let mut stats = self.stats.write();
701 stats.warnings.push(warning);
702 }
703 }
704 }
705
706 operation_sequences.extend(self.generate_operation_sequences());
708
709 hotspots.extend(self.identify_hotspot_bytecode(&stdlib_bytecode));
711
712 log::info!(
713 "Cached {} bytecode functions, {} sequences, {} hotspots",
714 stdlib_bytecode.len(),
715 operation_sequences.len(),
716 hotspots.len()
717 );
718
719 Ok(BytecodeCache {
720 stdlib_bytecode,
721 operation_sequences,
722 hotspots,
723 })
724 }
725
726 fn generate_operation_sequences(&self) -> Vec<BytecodeSequence> {
728 vec![
729 BytecodeSequence {
730 name: "scalar_add".to_string(),
731 bytecode: self.create_sequence_bytecode("x = a + b"),
732 usage_count: 10000,
733 average_execution_time: Duration::from_nanos(100),
734 },
735 BytecodeSequence {
736 name: "scalar_multiply".to_string(),
737 bytecode: self.create_sequence_bytecode("x = a * b"),
738 usage_count: 8000,
739 average_execution_time: Duration::from_nanos(120),
740 },
741 ]
742 }
743
744 fn create_sequence_bytecode(&self, source: &str) -> runmat_vm::Bytecode {
746 self.compile_source_to_bytecode(source)
747 .unwrap_or_else(|_| runmat_vm::Bytecode::empty())
748 }
749
750 fn compile_source_to_bytecode(&self, source: &str) -> Result<runmat_vm::Bytecode> {
751 let ast = runmat_parser::parse(source).map_err(|e| anyhow::anyhow!(e))?;
752 let lowering =
753 runmat_hir::lower(&ast, &LoweringContext::empty()).map_err(|e| anyhow::anyhow!(e))?;
754 self.compile_assembly_to_bytecode(&lowering.assembly)
755 }
756
757 fn compile_assembly_to_bytecode(
758 &self,
759 assembly: &runmat_hir::HirAssembly,
760 ) -> Result<runmat_vm::Bytecode> {
761 let entrypoint = assembly
762 .entrypoints
763 .first()
764 .ok_or_else(|| anyhow::anyhow!("semantic HIR assembly has no entrypoint"))?;
765 let mir = runmat_mir::lowering::lower_assembly(assembly).map_err(|err| {
766 anyhow::anyhow!(format!(
767 "failed to lower semantic HIR assembly to MIR: {err:?}"
768 ))
769 })?;
770 let _analysis = runmat_mir::analysis::analyze_assembly(&mir);
771 runmat_vm::compile(assembly, &mir, entrypoint.id).map_err(Into::into)
772 }
773
774 fn identify_hotspot_bytecode(
776 &self,
777 stdlib_bytecode: &HashMap<String, runmat_vm::Bytecode>,
778 ) -> Vec<HotspotBytecode> {
779 let mut hotspots = Vec::new();
780
781 for (name, bytecode) in stdlib_bytecode {
782 if self.is_hotspot_candidate(name, bytecode) {
783 hotspots.push(HotspotBytecode {
784 name: name.clone(),
785 bytecode: bytecode.clone(),
786 execution_frequency: self.estimate_execution_frequency(name),
787 jit_compilation_threshold: self.determine_jit_threshold(name),
788 optimization_hints: self.generate_bytecode_optimization_hints(name, bytecode),
789 });
790 }
791 }
792
793 hotspots
794 }
795
796 fn is_hotspot_candidate(&self, name: &str, bytecode: &runmat_vm::Bytecode) -> bool {
798 bytecode.instructions.len() > 10
800 || name.contains("loop")
801 || name.contains("mat")
802 || bytecode.instructions.iter().any(|instr| {
803 matches!(
804 instr,
805 runmat_vm::Instr::Jump(_) | runmat_vm::Instr::JumpIfFalse(_)
806 )
807 })
808 }
809
810 fn estimate_execution_frequency(&self, name: &str) -> u64 {
812 if name.contains("mat") || name.contains("linear") {
814 1000 } else if name.contains("loop") {
816 500 } else {
818 100 }
820 }
821
822 fn determine_jit_threshold(&self, name: &str) -> u32 {
824 if name.contains("mat") {
825 5 } else if name.contains("loop") {
827 10 } else {
829 20 }
831 }
832
833 fn generate_bytecode_optimization_hints(
835 &self,
836 name: &str,
837 _bytecode: &runmat_vm::Bytecode,
838 ) -> Vec<OptimizationHint> {
839 let mut hints = Vec::new();
840
841 if name.contains("mat") {
842 hints.push(OptimizationHint {
843 hint_type: "vectorization".to_string(),
844 parameters: [("target".to_string(), "simd".to_string())]
845 .iter()
846 .cloned()
847 .collect(),
848 expected_speedup: 4.0,
849 });
850 }
851
852 if name.contains("loop") {
853 hints.push(OptimizationHint {
854 hint_type: "loop_unrolling".to_string(),
855 parameters: [("factor".to_string(), "4".to_string())]
856 .iter()
857 .cloned()
858 .collect(),
859 expected_speedup: 2.0,
860 });
861 }
862
863 hints
864 }
865
866 fn build_gc_presets(&self) -> SnapshotResult<GcPresetCache> {
868 log::info!("Building GC preset cache");
869
870 let mut presets = HashMap::new();
871 let mut performance_profiles = HashMap::new();
872
873 presets.insert("default".to_string(), runmat_gc::GcConfig::default());
875 presets.insert(
876 "low-latency".to_string(),
877 runmat_gc::GcConfig::low_latency(),
878 );
879 presets.insert(
880 "high-throughput".to_string(),
881 runmat_gc::GcConfig::high_throughput(),
882 );
883 presets.insert("low-memory".to_string(), runmat_gc::GcConfig::low_memory());
884 presets.insert("debug".to_string(), runmat_gc::GcConfig::debug());
885
886 for preset_name in presets.keys() {
888 performance_profiles.insert(
889 preset_name.clone(),
890 self.create_gc_performance_profile(preset_name),
891 );
892 }
893
894 log::info!("Created {} GC presets", presets.len());
895
896 Ok(GcPresetCache {
897 presets,
898 default_preset: "default".to_string(),
899 performance_profiles,
900 })
901 }
902
903 fn create_gc_performance_profile(&self, preset_name: &str) -> GcPerformanceProfile {
905 match preset_name {
907 "low-latency" => GcPerformanceProfile {
908 average_allocation_rate: 1000000.0, average_collection_time: Duration::from_micros(100),
910 memory_overhead: 0.1,
911 throughput_impact: 0.05,
912 },
913 "high-throughput" => GcPerformanceProfile {
914 average_allocation_rate: 2000000.0,
915 average_collection_time: Duration::from_millis(10),
916 memory_overhead: 0.2,
917 throughput_impact: 0.02,
918 },
919 "low-memory" => GcPerformanceProfile {
920 average_allocation_rate: 500000.0,
921 average_collection_time: Duration::from_millis(5),
922 memory_overhead: 0.05,
923 throughput_impact: 0.1,
924 },
925 _ => GcPerformanceProfile {
926 average_allocation_rate: 800000.0,
927 average_collection_time: Duration::from_millis(2),
928 memory_overhead: 0.15,
929 throughput_impact: 0.08,
930 },
931 }
932 }
933
934 fn generate_optimization_hints(
936 &self,
937 snapshot: &Snapshot,
938 ) -> SnapshotResult<OptimizationHints> {
939 log::info!("Generating optimization hints");
940
941 let mut jit_hints = Vec::new();
942 let mut memory_hints = Vec::new();
943 let mut execution_hints = Vec::new();
944
945 for builtin in &snapshot.builtins.functions {
947 if matches!(
948 builtin.optimization_level,
949 OptimizationLevel::Aggressive | OptimizationLevel::MaxPerformance
950 ) {
951 jit_hints.push(JitHint {
952 pattern: builtin.name.clone(),
953 hint_type: self.determine_jit_hint_type(&builtin.category),
954 priority: builtin.optimization_level,
955 expected_performance_gain: self
956 .estimate_jit_performance_gain(&builtin.complexity),
957 });
958 }
959 }
960
961 memory_hints.extend(self.generate_memory_hints());
963
964 execution_hints.extend(self.generate_execution_hints(&snapshot.bytecode_cache));
966
967 log::info!(
968 "Generated {} JIT hints, {} memory hints, {} execution hints",
969 jit_hints.len(),
970 memory_hints.len(),
971 execution_hints.len()
972 );
973
974 Ok(OptimizationHints {
975 jit_hints,
976 memory_hints,
977 execution_hints,
978 })
979 }
980
981 fn determine_jit_hint_type(&self, category: &BuiltinCategory) -> JitHintType {
983 match category {
984 BuiltinCategory::LinearAlgebra | BuiltinCategory::MatrixOps => {
985 JitHintType::VectorizeCandidate
986 }
987 BuiltinCategory::Math | BuiltinCategory::Trigonometric => JitHintType::InlineCandidate,
988 _ => JitHintType::ConstantFolding,
989 }
990 }
991
992 fn estimate_jit_performance_gain(&self, complexity: &ComputationalComplexity) -> f64 {
994 match complexity {
995 ComputationalComplexity::Constant => 1.5,
996 ComputationalComplexity::Linear => 3.0,
997 ComputationalComplexity::Quadratic => 5.0,
998 ComputationalComplexity::Cubic => 8.0,
999 ComputationalComplexity::Exponential => 10.0,
1000 }
1001 }
1002
1003 fn generate_memory_hints(&self) -> Vec<MemoryHint> {
1005 vec![
1006 MemoryHint {
1007 data_structure: "matrix_data".to_string(),
1008 hint_type: MemoryHintType::AlignmentOptimization,
1009 alignment: 64, prefetch_pattern: PrefetchPattern::Sequential,
1011 },
1012 MemoryHint {
1013 data_structure: "builtin_dispatch".to_string(),
1014 hint_type: MemoryHintType::CacheLocalityOptimization,
1015 alignment: 8,
1016 prefetch_pattern: PrefetchPattern::Random,
1017 },
1018 ]
1019 }
1020
1021 fn generate_execution_hints(&self, bytecode_cache: &BytecodeCache) -> Vec<ExecutionHint> {
1023 let mut hints = Vec::new();
1024
1025 for hotspot in &bytecode_cache.hotspots {
1026 hints.push(ExecutionHint {
1027 pattern: hotspot.name.clone(),
1028 hint_type: ExecutionHintType::HotPath,
1029 frequency: hotspot.execution_frequency,
1030 optimization_potential: hotspot
1031 .optimization_hints
1032 .iter()
1033 .map(|h| h.expected_speedup)
1034 .fold(0.0, f64::max),
1035 });
1036 }
1037
1038 hints
1039 }
1040
1041 fn finalize_snapshot(&self, snapshot: &mut Snapshot) -> SnapshotResult<()> {
1043 log::info!("Finalizing snapshot");
1044
1045 let stats = self.stats.read();
1047 snapshot.metadata.performance_metrics = PerformanceMetrics {
1048 creation_time: stats
1049 .start_time
1050 .map_or(Duration::ZERO, |start| start.elapsed()),
1051 builtin_count: snapshot.builtins.functions.len() as u64,
1052 hir_cache_entries: snapshot.hir_cache.functions.len() as u64,
1053 bytecode_cache_entries: snapshot.bytecode_cache.stdlib_bytecode.len() as u64,
1054 uncompressed_size: bincode::serialized_size(snapshot).unwrap_or(0) as u64,
1055 compression_ratio: 1.0, peak_memory_usage: self.estimate_peak_memory_usage() as u64,
1057 };
1058
1059 Ok(())
1060 }
1061
1062 fn save_snapshot<P: AsRef<Path>>(
1064 &self,
1065 snapshot: &Snapshot,
1066 output_path: P,
1067 ) -> SnapshotResult<()> {
1068 log::info!("Saving snapshot to {}", output_path.as_ref().display());
1069
1070 let serialized = bincode::serialize(snapshot).map_err(SnapshotError::Serialization)?;
1072
1073 let uncompressed_size = serialized.len() as u64;
1075
1076 let (data, compression_info) = self.compress_snapshot_data(&serialized)?;
1078
1079 let mut header = SnapshotHeader::new(snapshot.metadata.clone());
1081
1082 header.data_info.compressed_size = data.len() as u64;
1084 header.data_info.uncompressed_size = uncompressed_size;
1085 header.data_info.compression = compression_info;
1086
1087 let mut format = SnapshotFormat::new(header, data);
1088
1089 #[cfg(feature = "validation")]
1091 if self.config.validation_enabled {
1092 format = format.with_checksum(crate::format::ChecksumAlgorithm::Sha256)?;
1093 }
1094
1095 self.write_snapshot_file(&mut format, output_path)?;
1097
1098 log::info!("Snapshot saved successfully");
1099 Ok(())
1100 }
1101
1102 fn compress_snapshot_data(
1103 &self,
1104 serialized: &[u8],
1105 ) -> SnapshotResult<(Vec<u8>, CompressionInfo)> {
1106 if !self.config.compression_enabled
1107 || matches!(
1108 self.config.compression_algorithm,
1109 crate::CompressionAlgorithm::None
1110 )
1111 {
1112 return Ok((
1113 serialized.to_vec(),
1114 CompressionInfo {
1115 algorithm: format::CompressionAlgorithm::None,
1116 level: 0,
1117 parameters: std::collections::HashMap::new(),
1118 },
1119 ));
1120 }
1121
1122 let mut compression = CompressionEngine::new(Self::compression_config_for(&self.config));
1123 let result = match self.config.compression_algorithm {
1124 crate::CompressionAlgorithm::Auto => compression.compress(serialized)?,
1125 crate::CompressionAlgorithm::Lz4 => compression.compress_with_algorithm(
1126 serialized,
1127 format::CompressionAlgorithm::Lz4 {
1128 fast: self.config.compression_level <= 3,
1129 },
1130 )?,
1131 crate::CompressionAlgorithm::Zstd => compression.compress_with_algorithm(
1132 serialized,
1133 format::CompressionAlgorithm::Zstd { dictionary: None },
1134 )?,
1135 crate::CompressionAlgorithm::None => unreachable!("handled before compression"),
1136 };
1137
1138 Ok((result.data, result.info))
1139 }
1140
1141 fn write_snapshot_file<P: AsRef<Path>>(
1143 &self,
1144 format: &mut SnapshotFormat,
1145 output_path: P,
1146 ) -> SnapshotResult<()> {
1147 use std::io::Write;
1148
1149 let mut file = std::fs::File::create(output_path)?;
1150
1151 let (header_data, header_size) = Self::encode_header_with_offset(&mut format.header)?;
1153
1154 file.write_all(&header_size.to_le_bytes())?;
1156
1157 file.write_all(&header_data)?;
1159
1160 file.write_all(&format.data)?;
1162
1163 if let Some(checksum) = &format.checksum {
1165 file.write_all(checksum)?;
1166 }
1167
1168 file.sync_all()?;
1169 Ok(())
1170 }
1171
1172 fn encode_header_with_offset(header: &mut SnapshotHeader) -> SnapshotResult<(Vec<u8>, u32)> {
1173 const MAX_ITER: usize = 4;
1174 let mut last_size = None;
1175
1176 for _ in 0..MAX_ITER {
1177 let header_data = bincode::serialize(header)?;
1178 let header_size = header_data.len() as u32;
1179 let desired_offset = 4 + header_size as u64;
1180
1181 if header.data_info.data_offset == desired_offset {
1182 return Ok((header_data, header_size));
1183 }
1184
1185 header.data_info.data_offset = desired_offset;
1186 last_size = Some(header_size);
1187 }
1188
1189 Err(SnapshotError::Configuration {
1190 message: format!(
1191 "Snapshot header failed to stabilize data_offset after {MAX_ITER} attempts (last observed size: {:?})",
1192 last_size
1193 ),
1194 })
1195 }
1196
1197 fn start_build(&self) {
1199 {
1200 let mut stats = self.stats.write();
1201 stats.start_time = Some(Instant::now());
1202 }
1203
1204 log::info!("Starting snapshot build");
1205
1206 if let Some(ref progress) = self.progress {
1207 progress.set_message("Initializing...");
1208 }
1209 }
1210
1211 fn finish_build(&self) {
1213 if let Some(ref progress) = self.progress {
1214 progress.finish_with_message("Snapshot build completed!");
1215 }
1216
1217 let stats = self.stats.read();
1218 if let Some(start_time) = stats.start_time {
1219 let total_time = start_time.elapsed();
1220 log::info!("Snapshot build completed in {total_time:?}");
1221 }
1222 }
1223
1224 fn update_progress(&self, current: u64, total: u64, message: &str) {
1226 if let Some(ref progress) = self.progress {
1227 progress.set_position((current * 100) / total);
1228 progress.set_message(message.to_string());
1229 }
1230 }
1231
1232 fn estimate_peak_memory_usage(&self) -> usize {
1234 std::mem::size_of::<Snapshot>() * 2 }
1237
1238 pub fn stats(&self) -> BuildStats {
1240 let stats = self.stats.read();
1242 BuildStats {
1243 start_time: stats.start_time,
1244 phase_times: stats.phase_times.clone(),
1245 memory_usage: stats.memory_usage.clone(),
1246 items_processed: stats.items_processed.clone(),
1247 errors: stats.errors.clone(),
1248 warnings: stats.warnings.clone(),
1249 }
1250 }
1251}
1252
1253fn cap_optimization_level(
1254 inferred: OptimizationLevel,
1255 max_level: OptimizationLevel,
1256) -> OptimizationLevel {
1257 if optimization_rank(inferred) > optimization_rank(max_level) {
1258 max_level
1259 } else {
1260 inferred
1261 }
1262}
1263
1264fn optimization_rank(level: OptimizationLevel) -> u8 {
1265 match level {
1266 OptimizationLevel::None => 0,
1267 OptimizationLevel::Basic => 1,
1268 OptimizationLevel::Aggressive => 2,
1269 OptimizationLevel::MaxPerformance => 3,
1270 }
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275 use super::*;
1276
1277 #[test]
1278 fn test_snapshot_builder_creation() {
1279 let config = SnapshotConfig::default();
1280 let builder = SnapshotBuilder::new(config);
1281
1282 let stats = builder.stats();
1283 assert!(stats.start_time.is_none());
1284 assert!(stats.errors.is_empty());
1285 }
1286
1287 #[test]
1288 fn test_builtin_analysis() {
1289 let config = SnapshotConfig::default();
1290 let builder = SnapshotBuilder::new(config);
1291
1292 fn test_builtin(_args: &[runmat_builtins::Value]) -> runmat_builtins::BuiltinFuture {
1293 Box::pin(async { Ok(runmat_builtins::Value::Num(0.0)) })
1294 }
1295
1296 let builtin = runmat_builtins::BuiltinFunction::new(
1297 "matmul",
1298 "Test builtin function",
1299 "Category",
1300 "",
1301 "",
1302 vec![],
1303 runmat_builtins::Type::Num,
1304 None,
1305 test_builtin,
1306 &[],
1307 false,
1308 false,
1309 );
1310
1311 let metadata = builder.analyze_builtin_function(&builtin).unwrap();
1312 assert_eq!(metadata.name, "matmul");
1313 assert!(matches!(metadata.category, BuiltinCategory::LinearAlgebra));
1314 assert!(matches!(
1315 metadata.complexity,
1316 ComputationalComplexity::Cubic
1317 ));
1318 }
1319
1320 #[test]
1321 fn test_category_inference() {
1322 let config = SnapshotConfig::default();
1323 let builder = SnapshotBuilder::new(config);
1324
1325 assert!(matches!(
1326 builder.infer_builtin_category("sin"),
1327 BuiltinCategory::Trigonometric
1328 ));
1329 assert!(matches!(
1330 builder.infer_builtin_category("matmul"),
1331 BuiltinCategory::LinearAlgebra
1332 ));
1333 assert!(matches!(
1334 builder.infer_builtin_category("max"),
1335 BuiltinCategory::Comparison
1336 ));
1337 }
1338
1339 #[test]
1340 fn test_complexity_inference() {
1341 let config = SnapshotConfig::default();
1342 let builder = SnapshotBuilder::new(config);
1343
1344 assert!(matches!(
1345 builder.infer_computational_complexity("matmul"),
1346 ComputationalComplexity::Cubic
1347 ));
1348 assert!(matches!(
1349 builder.infer_computational_complexity("dot"),
1350 ComputationalComplexity::Linear
1351 ));
1352 assert!(matches!(
1353 builder.infer_computational_complexity("abs"),
1354 ComputationalComplexity::Constant
1355 ));
1356 }
1357
1358 #[test]
1359 fn test_optimization_level_respects_config_cap() {
1360 let config = SnapshotConfig {
1361 max_optimization_level: OptimizationLevel::Basic,
1362 ..SnapshotConfig::default()
1363 };
1364 let builder = SnapshotBuilder::new(config);
1365
1366 assert_eq!(
1367 builder.infer_optimization_level("matmul", &BuiltinCategory::LinearAlgebra),
1368 OptimizationLevel::Basic
1369 );
1370 assert_eq!(
1371 builder.infer_optimization_level("sin", &BuiltinCategory::Trigonometric),
1372 OptimizationLevel::Basic
1373 );
1374 assert_eq!(
1375 builder.infer_optimization_level("mean", &BuiltinCategory::Statistics),
1376 OptimizationLevel::Basic
1377 );
1378 assert_eq!(
1379 builder.infer_optimization_level("disp", &BuiltinCategory::Utility),
1380 OptimizationLevel::None
1381 );
1382 }
1383
1384 #[test]
1385 fn test_empty_snapshot_creation() {
1386 let config = SnapshotConfig::default();
1387 let builder = SnapshotBuilder::new(config);
1388
1389 let snapshot = builder.create_empty_snapshot();
1390 assert!(snapshot.builtins.functions.is_empty());
1391 assert!(snapshot.hir_cache.functions.is_empty());
1392 assert!(snapshot.bytecode_cache.stdlib_bytecode.is_empty());
1393 }
1394
1395 #[test]
1396 fn test_gc_performance_profile() {
1397 let config = SnapshotConfig::default();
1398 let builder = SnapshotBuilder::new(config);
1399
1400 let profile = builder.create_gc_performance_profile("low-latency");
1401 assert!(profile.average_collection_time < Duration::from_millis(1));
1402 assert!(profile.memory_overhead < 0.2);
1403 }
1404
1405 #[test]
1406 fn test_build_phases() {
1407 let phases = SnapshotBuilder::test_all_phases();
1409 assert_eq!(phases.len(), 10);
1410
1411 for phase in &phases {
1413 let requirements = SnapshotBuilder::analyze_phase_requirements(phase);
1414 assert!(!requirements.is_empty());
1415
1416 match phase {
1418 BuildPhase::Compression => assert!(phase.needs_compression()),
1419 BuildPhase::Validation => assert!(phase.needs_validation()),
1420 BuildPhase::Serialization => assert!(phase.involves_serialization()),
1421 BuildPhase::Finalization => assert!(phase.involves_serialization()),
1422 _ => {
1423 assert!(!phase.needs_compression());
1424 assert!(!phase.needs_validation());
1425 }
1426 }
1427 }
1428 }
1429
1430 #[test]
1431 fn test_compression_engine_access() {
1432 let config = SnapshotConfig::default();
1433 let builder = SnapshotBuilder::new(config);
1434
1435 let _engine = builder.compression_engine();
1437 }
1438
1439 #[cfg(feature = "validation")]
1440 #[test]
1441 fn test_validator_access() {
1442 let config = SnapshotConfig::default();
1443 let builder = SnapshotBuilder::new(config);
1444
1445 let _validator = builder.validator();
1447 }
1448}