use runmat_time::Instant;
use std::collections::{hash_map::Entry, HashMap};
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{Context, Result};
use indicatif::{ProgressBar, ProgressStyle};
use parking_lot::RwLock;
use crate::compression::{CompressionConfig, CompressionEngine};
use crate::format::*;
use crate::validation::SnapshotValidator;
use crate::*;
use runmat_hir::LoweringContext;
pub struct SnapshotBuilder {
config: SnapshotConfig,
compression: CompressionEngine,
#[cfg(feature = "validation")]
validator: SnapshotValidator,
stats: Arc<RwLock<BuildStats>>,
progress: Option<ProgressBar>,
}
#[derive(Debug, Default)]
pub struct BuildStats {
pub start_time: Option<Instant>,
pub phase_times: HashMap<String, Duration>,
pub memory_usage: Vec<(String, usize)>,
pub items_processed: HashMap<String, usize>,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone)]
pub enum BuildPhase {
Initialization,
BuiltinRegistration,
HirCaching,
BytecodeCaching,
GcPresetCaching,
OptimizationAnalysis,
Compression,
Validation,
Serialization,
Finalization,
}
impl BuildPhase {
fn name(&self) -> &'static str {
match self {
BuildPhase::Initialization => "Initialization",
BuildPhase::BuiltinRegistration => "Builtin Registration",
BuildPhase::HirCaching => "HIR Caching",
BuildPhase::BytecodeCaching => "Bytecode Caching",
BuildPhase::GcPresetCaching => "GC Preset Caching",
BuildPhase::OptimizationAnalysis => "Optimization Analysis",
BuildPhase::Compression => "Compression",
BuildPhase::Validation => "Validation",
BuildPhase::Serialization => "Serialization",
BuildPhase::Finalization => "Finalization",
}
}
fn weight(&self) -> u64 {
match self {
BuildPhase::Initialization => 5,
BuildPhase::BuiltinRegistration => 15,
BuildPhase::HirCaching => 20,
BuildPhase::BytecodeCaching => 25,
BuildPhase::GcPresetCaching => 5,
BuildPhase::OptimizationAnalysis => 10,
BuildPhase::Compression => 10,
BuildPhase::Validation => 5,
BuildPhase::Serialization => 3,
BuildPhase::Finalization => 2,
}
}
pub fn needs_compression(&self) -> bool {
matches!(self, BuildPhase::Compression)
}
pub fn needs_validation(&self) -> bool {
matches!(self, BuildPhase::Validation)
}
pub fn involves_serialization(&self) -> bool {
matches!(self, BuildPhase::Serialization | BuildPhase::Finalization)
}
}
impl SnapshotBuilder {
pub fn new(config: SnapshotConfig) -> Self {
let compression_config = Self::compression_config_for(&config);
let compression = CompressionEngine::new(compression_config);
#[cfg(feature = "validation")]
let validator = SnapshotValidator::new();
let progress = if config.progress_reporting {
let pb = ProgressBar::new(100);
pb.set_style(
ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>3}/{len:3} {msg}")
.unwrap()
.progress_chars("#>-"),
);
Some(pb)
} else {
None
};
Self {
config,
compression,
#[cfg(feature = "validation")]
validator,
stats: Arc::new(RwLock::new(BuildStats::default())),
progress,
}
}
fn compression_config_for(config: &SnapshotConfig) -> CompressionConfig {
CompressionConfig {
default_level: config.compression_level,
adaptive_selection: matches!(
config.compression_algorithm,
crate::CompressionAlgorithm::Auto
),
prefer_speed: matches!(
config.compression_algorithm,
crate::CompressionAlgorithm::Lz4
) || config.compression_level <= 3,
..CompressionConfig::default()
}
}
pub fn build_and_save<P: AsRef<Path>>(&self, output_path: P) -> SnapshotResult<()> {
let snapshot = self.build()?;
self.save_snapshot(&snapshot, output_path)
}
pub fn compression_engine(&self) -> &CompressionEngine {
&self.compression
}
#[cfg(feature = "validation")]
pub fn validator(&self) -> &SnapshotValidator {
&self.validator
}
#[cfg(test)]
pub fn test_all_phases() -> Vec<BuildPhase> {
vec![
BuildPhase::Initialization,
BuildPhase::BuiltinRegistration,
BuildPhase::HirCaching,
BuildPhase::BytecodeCaching,
BuildPhase::GcPresetCaching,
BuildPhase::OptimizationAnalysis,
BuildPhase::Compression,
BuildPhase::Validation,
BuildPhase::Serialization,
BuildPhase::Finalization,
]
}
pub fn analyze_phase_requirements(phase: &BuildPhase) -> String {
let mut requirements = Vec::new();
if phase.needs_compression() {
requirements.push("compression engine");
}
if phase.needs_validation() {
requirements.push("validation framework");
}
if phase.involves_serialization() {
requirements.push("serialization support");
}
if requirements.is_empty() {
"No special requirements".to_string()
} else {
format!("Requires: {}", requirements.join(", "))
}
}
pub fn build(&self) -> SnapshotResult<Snapshot> {
self.start_build();
let phases = [
BuildPhase::Initialization,
BuildPhase::BuiltinRegistration,
BuildPhase::HirCaching,
BuildPhase::BytecodeCaching,
BuildPhase::GcPresetCaching,
BuildPhase::OptimizationAnalysis,
BuildPhase::Finalization,
];
let mut current_progress = 0u64;
let total_progress: u64 = phases.iter().map(|p| p.weight()).sum();
let mut snapshot = self.execute_phase(BuildPhase::Initialization, || {
Ok(self.create_empty_snapshot())
})?;
current_progress += BuildPhase::Initialization.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::Initialization.name(),
);
snapshot.builtins = self.execute_phase(BuildPhase::BuiltinRegistration, || {
self.build_builtin_registry()
})?;
current_progress += BuildPhase::BuiltinRegistration.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::BuiltinRegistration.name(),
);
snapshot.hir_cache =
self.execute_phase(BuildPhase::HirCaching, || self.build_hir_cache())?;
current_progress += BuildPhase::HirCaching.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::HirCaching.name(),
);
snapshot.bytecode_cache = self.execute_phase(BuildPhase::BytecodeCaching, || {
self.build_bytecode_cache(&snapshot.hir_cache)
})?;
current_progress += BuildPhase::BytecodeCaching.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::BytecodeCaching.name(),
);
snapshot.gc_presets =
self.execute_phase(BuildPhase::GcPresetCaching, || self.build_gc_presets())?;
current_progress += BuildPhase::GcPresetCaching.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::GcPresetCaching.name(),
);
snapshot.optimization_hints = self
.execute_phase(BuildPhase::OptimizationAnalysis, || {
self.generate_optimization_hints(&snapshot)
})?;
current_progress += BuildPhase::OptimizationAnalysis.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::OptimizationAnalysis.name(),
);
self.execute_phase(BuildPhase::Finalization, || {
self.finalize_snapshot(&mut snapshot)
})?;
current_progress += BuildPhase::Finalization.weight();
self.update_progress(
current_progress,
total_progress,
BuildPhase::Finalization.name(),
);
self.finish_build();
Ok(snapshot)
}
fn execute_phase<T, F>(&self, phase: BuildPhase, f: F) -> SnapshotResult<T>
where
F: FnOnce() -> SnapshotResult<T>,
{
let start = Instant::now();
log::info!("Starting build phase: {}", phase.name());
let result = f().context(format!("Failed in phase: {}", phase.name()));
let duration = start.elapsed();
{
let mut stats = self.stats.write();
stats.phase_times.insert(phase.name().to_string(), duration);
match &result {
Ok(_) => {
log::info!("Completed build phase: {} in {:?}", phase.name(), duration);
}
Err(e) => {
let error_msg = format!("Failed in phase {}: {}", phase.name(), e);
log::error!("{error_msg}");
stats.errors.push(error_msg);
}
}
}
result.map_err(|e| SnapshotError::Configuration {
message: e.to_string(),
})
}
fn create_empty_snapshot(&self) -> Snapshot {
Snapshot {
metadata: SnapshotMetadata::current(),
builtins: BuiltinRegistry {
name_index: HashMap::new(),
functions: Vec::new(),
dispatch_table: Arc::new(RwLock::new(Vec::new())),
},
hir_cache: HirCache {
functions: HashMap::new(),
patterns: Vec::new(),
type_cache: HashMap::new(),
},
bytecode_cache: BytecodeCache {
stdlib_bytecode: HashMap::new(),
operation_sequences: Vec::new(),
hotspots: Vec::new(),
},
gc_presets: GcPresetCache {
presets: HashMap::new(),
default_preset: "default".to_string(),
performance_profiles: HashMap::new(),
},
optimization_hints: OptimizationHints {
jit_hints: Vec::new(),
memory_hints: Vec::new(),
execution_hints: Vec::new(),
},
}
}
fn build_builtin_registry(&self) -> SnapshotResult<BuiltinRegistry> {
log::info!("Building builtin function registry");
let builtins = runmat_builtins::builtin_functions();
let mut name_index = HashMap::new();
let mut functions = Vec::new();
let mut dispatch_table = Vec::new();
for (index, builtin) in builtins.iter().enumerate() {
match name_index.entry(builtin.name.to_string()) {
Entry::Vacant(slot) => {
slot.insert(index);
}
Entry::Occupied(existing) => {
log::warn!(
"Duplicate builtin '{}' detected while building snapshot (first index {}, duplicate index {})",
builtin.name,
existing.get(),
index
);
}
}
let metadata = self.analyze_builtin_function(builtin)?;
functions.push(metadata);
dispatch_table.push(builtin.implementation);
{
let mut stats = self.stats.write();
*stats
.items_processed
.entry("builtins".to_string())
.or_insert(0) += 1;
}
}
log::info!("Registered {} builtin functions", functions.len());
Ok(BuiltinRegistry {
name_index,
functions,
dispatch_table: Arc::new(RwLock::new(dispatch_table)),
})
}
fn analyze_builtin_function(
&self,
builtin: &runmat_builtins::BuiltinFunction,
) -> SnapshotResult<BuiltinMetadata> {
let category = self.infer_builtin_category(builtin.name);
let complexity = self.infer_computational_complexity(builtin.name);
let optimization_level = self.infer_optimization_level(builtin.name, &category);
let arity = if builtin.name.ends_with("mul") || builtin.name.contains("dot") {
BuiltinArity::Exact(2)
} else if builtin.name == "norm"
|| builtin.name.starts_with("sin")
|| builtin.name.starts_with("cos")
{
BuiltinArity::Exact(1)
} else {
BuiltinArity::Range(1, 3)
};
Ok(BuiltinMetadata {
name: builtin.name.to_string(),
arity,
category,
complexity,
optimization_level,
})
}
fn infer_builtin_category(&self, name: &str) -> BuiltinCategory {
if name.contains("sin")
|| name.contains("cos")
|| name.contains("tan")
|| name.contains("atan")
|| name.contains("asin")
|| name.contains("acos")
{
BuiltinCategory::Trigonometric
} else if name.contains("mat")
|| name.contains("dot")
|| name.contains("norm")
|| name.contains("inv")
|| name.contains("det")
{
BuiltinCategory::LinearAlgebra
} else if name.contains("mean") || name.contains("std") || name.contains("var") {
BuiltinCategory::Statistics
} else if name.contains("transpose") || name.contains("reshape") || name.contains("size") {
BuiltinCategory::MatrixOps
} else if name == "max"
|| name == "min"
|| name.contains("equal")
|| name.contains("greater")
|| name.contains("less")
{
BuiltinCategory::Comparison
} else if name.contains("sqrt")
|| name.contains("exp")
|| name.contains("log")
|| name.contains("abs")
|| name.contains("pow")
{
BuiltinCategory::Math
} else {
BuiltinCategory::Utility
}
}
fn infer_computational_complexity(&self, name: &str) -> ComputationalComplexity {
if name.contains("matmul") || name.contains("inv") || name.contains("det") {
ComputationalComplexity::Cubic
} else if name.contains("mat") && !name.contains("matmul") {
ComputationalComplexity::Quadratic
} else if name.contains("dot") || name.contains("norm") || name.contains("sum") {
ComputationalComplexity::Linear
} else {
ComputationalComplexity::Constant
}
}
fn infer_optimization_level(
&self,
_name: &str,
category: &BuiltinCategory,
) -> OptimizationLevel {
let inferred = match category {
BuiltinCategory::LinearAlgebra | BuiltinCategory::MatrixOps => {
OptimizationLevel::MaxPerformance
}
BuiltinCategory::Math | BuiltinCategory::Trigonometric => OptimizationLevel::Aggressive,
BuiltinCategory::Statistics => OptimizationLevel::Basic,
_ => OptimizationLevel::None,
};
cap_optimization_level(inferred, self.config.max_optimization_level)
}
fn build_hir_cache(&self) -> SnapshotResult<HirCache> {
log::info!("Building HIR cache");
let mut functions = HashMap::new();
let mut patterns = Vec::new();
let mut type_cache = HashMap::new();
let stdlib_functions = self.get_stdlib_function_sources();
for (name, source) in stdlib_functions {
match self.compile_to_hir(&source) {
Ok(hir) => {
self.extract_type_info(&hir, &mut type_cache);
functions.insert(name.clone(), hir);
{
let mut stats = self.stats.write();
*stats
.items_processed
.entry("hir_functions".to_string())
.or_insert(0) += 1;
}
}
Err(e) => {
let warning = format!("Failed to compile {name} to HIR: {e}");
log::warn!("{warning}");
let mut stats = self.stats.write();
stats.warnings.push(warning);
}
}
}
patterns.extend(self.generate_common_patterns());
log::info!(
"Cached {} HIR functions and {} patterns",
functions.len(),
patterns.len()
);
Ok(HirCache {
functions,
patterns,
type_cache,
})
}
fn get_stdlib_function_sources(&self) -> Vec<(String, String)> {
vec![
(
"zeros".to_string(),
"function z = zeros(m, n); z = zeros(m, n); end".to_string(),
),
(
"ones".to_string(),
"function o = ones(m, n); o = ones(m, n); end".to_string(),
),
(
"eye".to_string(),
"function i = eye(n); i = eye(n); end".to_string(),
),
(
"sum_vec".to_string(),
"function s = sum_vec(v); s = 0; for i = 1:length(v); s = s + v(i); end; end"
.to_string(),
),
(
"mean_vec".to_string(),
"function m = mean_vec(v); m = sum_vec(v) / length(v); end".to_string(),
),
]
}
fn compile_to_hir(&self, source: &str) -> Result<runmat_hir::HirAssembly> {
let ast = runmat_parser::parse(source).map_err(|e| anyhow::anyhow!(e))?;
let hir =
runmat_hir::lower(&ast, &LoweringContext::empty()).map_err(|e| anyhow::anyhow!(e))?;
Ok(hir.assembly)
}
fn extract_type_info(
&self,
_hir: &runmat_hir::HirAssembly,
_type_cache: &mut HashMap<String, runmat_hir::Type>,
) {
}
fn generate_common_patterns(&self) -> Vec<HirPattern> {
vec![
HirPattern {
name: "simple_for_loop".to_string(),
pattern: self.create_pattern_hir("for i = 1:n; x = x + 1; end"),
frequency: 1000,
optimization_priority: OptimizationLevel::Aggressive,
},
HirPattern {
name: "matrix_multiply".to_string(),
pattern: self.create_pattern_hir("C = A * B"),
frequency: 500,
optimization_priority: OptimizationLevel::MaxPerformance,
},
]
}
fn create_pattern_hir(&self, source: &str) -> runmat_hir::HirAssembly {
self.compile_to_hir(source).unwrap_or_else(|_| {
runmat_hir::HirAssembly::default()
})
}
fn build_bytecode_cache(&self, hir_cache: &HirCache) -> SnapshotResult<BytecodeCache> {
log::info!("Building bytecode cache");
let mut stdlib_bytecode = HashMap::new();
let mut operation_sequences = Vec::new();
let mut hotspots = Vec::new();
let stdlib_sources: HashMap<_, _> =
self.get_stdlib_function_sources().into_iter().collect();
for (name, hir) in &hir_cache.functions {
let compiled = stdlib_sources
.get(name)
.map(|source| self.compile_source_to_bytecode(source))
.unwrap_or_else(|| self.compile_assembly_to_bytecode(hir));
match compiled {
Ok(bytecode) => {
stdlib_bytecode.insert(name.clone(), bytecode);
{
let mut stats = self.stats.write();
*stats
.items_processed
.entry("bytecode_functions".to_string())
.or_insert(0) += 1;
}
}
Err(e) => {
let warning = format!("Failed to compile {name} to bytecode: {e}");
log::warn!("{warning}");
let mut stats = self.stats.write();
stats.warnings.push(warning);
}
}
}
operation_sequences.extend(self.generate_operation_sequences());
hotspots.extend(self.identify_hotspot_bytecode(&stdlib_bytecode));
log::info!(
"Cached {} bytecode functions, {} sequences, {} hotspots",
stdlib_bytecode.len(),
operation_sequences.len(),
hotspots.len()
);
Ok(BytecodeCache {
stdlib_bytecode,
operation_sequences,
hotspots,
})
}
fn generate_operation_sequences(&self) -> Vec<BytecodeSequence> {
vec![
BytecodeSequence {
name: "scalar_add".to_string(),
bytecode: self.create_sequence_bytecode("x = a + b"),
usage_count: 10000,
average_execution_time: Duration::from_nanos(100),
},
BytecodeSequence {
name: "scalar_multiply".to_string(),
bytecode: self.create_sequence_bytecode("x = a * b"),
usage_count: 8000,
average_execution_time: Duration::from_nanos(120),
},
]
}
fn create_sequence_bytecode(&self, source: &str) -> runmat_vm::Bytecode {
self.compile_source_to_bytecode(source)
.unwrap_or_else(|_| runmat_vm::Bytecode::empty())
}
fn compile_source_to_bytecode(&self, source: &str) -> Result<runmat_vm::Bytecode> {
let ast = runmat_parser::parse(source).map_err(|e| anyhow::anyhow!(e))?;
let lowering =
runmat_hir::lower(&ast, &LoweringContext::empty()).map_err(|e| anyhow::anyhow!(e))?;
self.compile_assembly_to_bytecode(&lowering.assembly)
}
fn compile_assembly_to_bytecode(
&self,
assembly: &runmat_hir::HirAssembly,
) -> Result<runmat_vm::Bytecode> {
let entrypoint = assembly
.entrypoints
.first()
.ok_or_else(|| anyhow::anyhow!("semantic HIR assembly has no entrypoint"))?;
let mir = runmat_mir::lowering::lower_assembly(assembly).map_err(|err| {
anyhow::anyhow!(format!(
"failed to lower semantic HIR assembly to MIR: {err:?}"
))
})?;
let _analysis = runmat_mir::analysis::analyze_assembly(&mir);
runmat_vm::compile(assembly, &mir, entrypoint.id).map_err(Into::into)
}
fn identify_hotspot_bytecode(
&self,
stdlib_bytecode: &HashMap<String, runmat_vm::Bytecode>,
) -> Vec<HotspotBytecode> {
let mut hotspots = Vec::new();
for (name, bytecode) in stdlib_bytecode {
if self.is_hotspot_candidate(name, bytecode) {
hotspots.push(HotspotBytecode {
name: name.clone(),
bytecode: bytecode.clone(),
execution_frequency: self.estimate_execution_frequency(name),
jit_compilation_threshold: self.determine_jit_threshold(name),
optimization_hints: self.generate_bytecode_optimization_hints(name, bytecode),
});
}
}
hotspots
}
fn is_hotspot_candidate(&self, name: &str, bytecode: &runmat_vm::Bytecode) -> bool {
bytecode.instructions.len() > 10
|| name.contains("loop")
|| name.contains("mat")
|| bytecode.instructions.iter().any(|instr| {
matches!(
instr,
runmat_vm::Instr::Jump(_) | runmat_vm::Instr::JumpIfFalse(_)
)
})
}
fn estimate_execution_frequency(&self, name: &str) -> u64 {
if name.contains("mat") || name.contains("linear") {
1000 } else if name.contains("loop") {
500 } else {
100 }
}
fn determine_jit_threshold(&self, name: &str) -> u32 {
if name.contains("mat") {
5 } else if name.contains("loop") {
10 } else {
20 }
}
fn generate_bytecode_optimization_hints(
&self,
name: &str,
_bytecode: &runmat_vm::Bytecode,
) -> Vec<OptimizationHint> {
let mut hints = Vec::new();
if name.contains("mat") {
hints.push(OptimizationHint {
hint_type: "vectorization".to_string(),
parameters: [("target".to_string(), "simd".to_string())]
.iter()
.cloned()
.collect(),
expected_speedup: 4.0,
});
}
if name.contains("loop") {
hints.push(OptimizationHint {
hint_type: "loop_unrolling".to_string(),
parameters: [("factor".to_string(), "4".to_string())]
.iter()
.cloned()
.collect(),
expected_speedup: 2.0,
});
}
hints
}
fn build_gc_presets(&self) -> SnapshotResult<GcPresetCache> {
log::info!("Building GC preset cache");
let mut presets = HashMap::new();
let mut performance_profiles = HashMap::new();
presets.insert("default".to_string(), runmat_gc::GcConfig::default());
presets.insert(
"low-latency".to_string(),
runmat_gc::GcConfig::low_latency(),
);
presets.insert(
"high-throughput".to_string(),
runmat_gc::GcConfig::high_throughput(),
);
presets.insert("low-memory".to_string(), runmat_gc::GcConfig::low_memory());
presets.insert("debug".to_string(), runmat_gc::GcConfig::debug());
for preset_name in presets.keys() {
performance_profiles.insert(
preset_name.clone(),
self.create_gc_performance_profile(preset_name),
);
}
log::info!("Created {} GC presets", presets.len());
Ok(GcPresetCache {
presets,
default_preset: "default".to_string(),
performance_profiles,
})
}
fn create_gc_performance_profile(&self, preset_name: &str) -> GcPerformanceProfile {
match preset_name {
"low-latency" => GcPerformanceProfile {
average_allocation_rate: 1000000.0, average_collection_time: Duration::from_micros(100),
memory_overhead: 0.1,
throughput_impact: 0.05,
},
"high-throughput" => GcPerformanceProfile {
average_allocation_rate: 2000000.0,
average_collection_time: Duration::from_millis(10),
memory_overhead: 0.2,
throughput_impact: 0.02,
},
"low-memory" => GcPerformanceProfile {
average_allocation_rate: 500000.0,
average_collection_time: Duration::from_millis(5),
memory_overhead: 0.05,
throughput_impact: 0.1,
},
_ => GcPerformanceProfile {
average_allocation_rate: 800000.0,
average_collection_time: Duration::from_millis(2),
memory_overhead: 0.15,
throughput_impact: 0.08,
},
}
}
fn generate_optimization_hints(
&self,
snapshot: &Snapshot,
) -> SnapshotResult<OptimizationHints> {
log::info!("Generating optimization hints");
let mut jit_hints = Vec::new();
let mut memory_hints = Vec::new();
let mut execution_hints = Vec::new();
for builtin in &snapshot.builtins.functions {
if matches!(
builtin.optimization_level,
OptimizationLevel::Aggressive | OptimizationLevel::MaxPerformance
) {
jit_hints.push(JitHint {
pattern: builtin.name.clone(),
hint_type: self.determine_jit_hint_type(&builtin.category),
priority: builtin.optimization_level,
expected_performance_gain: self
.estimate_jit_performance_gain(&builtin.complexity),
});
}
}
memory_hints.extend(self.generate_memory_hints());
execution_hints.extend(self.generate_execution_hints(&snapshot.bytecode_cache));
log::info!(
"Generated {} JIT hints, {} memory hints, {} execution hints",
jit_hints.len(),
memory_hints.len(),
execution_hints.len()
);
Ok(OptimizationHints {
jit_hints,
memory_hints,
execution_hints,
})
}
fn determine_jit_hint_type(&self, category: &BuiltinCategory) -> JitHintType {
match category {
BuiltinCategory::LinearAlgebra | BuiltinCategory::MatrixOps => {
JitHintType::VectorizeCandidate
}
BuiltinCategory::Math | BuiltinCategory::Trigonometric => JitHintType::InlineCandidate,
_ => JitHintType::ConstantFolding,
}
}
fn estimate_jit_performance_gain(&self, complexity: &ComputationalComplexity) -> f64 {
match complexity {
ComputationalComplexity::Constant => 1.5,
ComputationalComplexity::Linear => 3.0,
ComputationalComplexity::Quadratic => 5.0,
ComputationalComplexity::Cubic => 8.0,
ComputationalComplexity::Exponential => 10.0,
}
}
fn generate_memory_hints(&self) -> Vec<MemoryHint> {
vec![
MemoryHint {
data_structure: "matrix_data".to_string(),
hint_type: MemoryHintType::AlignmentOptimization,
alignment: 64, prefetch_pattern: PrefetchPattern::Sequential,
},
MemoryHint {
data_structure: "builtin_dispatch".to_string(),
hint_type: MemoryHintType::CacheLocalityOptimization,
alignment: 8,
prefetch_pattern: PrefetchPattern::Random,
},
]
}
fn generate_execution_hints(&self, bytecode_cache: &BytecodeCache) -> Vec<ExecutionHint> {
let mut hints = Vec::new();
for hotspot in &bytecode_cache.hotspots {
hints.push(ExecutionHint {
pattern: hotspot.name.clone(),
hint_type: ExecutionHintType::HotPath,
frequency: hotspot.execution_frequency,
optimization_potential: hotspot
.optimization_hints
.iter()
.map(|h| h.expected_speedup)
.fold(0.0, f64::max),
});
}
hints
}
fn finalize_snapshot(&self, snapshot: &mut Snapshot) -> SnapshotResult<()> {
log::info!("Finalizing snapshot");
let stats = self.stats.read();
snapshot.metadata.performance_metrics = PerformanceMetrics {
creation_time: stats
.start_time
.map_or(Duration::ZERO, |start| start.elapsed()),
builtin_count: snapshot.builtins.functions.len() as u64,
hir_cache_entries: snapshot.hir_cache.functions.len() as u64,
bytecode_cache_entries: snapshot.bytecode_cache.stdlib_bytecode.len() as u64,
uncompressed_size: bincode::serialized_size(snapshot).unwrap_or(0) as u64,
compression_ratio: 1.0, peak_memory_usage: self.estimate_peak_memory_usage() as u64,
};
Ok(())
}
fn save_snapshot<P: AsRef<Path>>(
&self,
snapshot: &Snapshot,
output_path: P,
) -> SnapshotResult<()> {
log::info!("Saving snapshot to {}", output_path.as_ref().display());
let serialized = bincode::serialize(snapshot).map_err(SnapshotError::Serialization)?;
let uncompressed_size = serialized.len() as u64;
let (data, compression_info) = self.compress_snapshot_data(&serialized)?;
let mut header = SnapshotHeader::new(snapshot.metadata.clone());
header.data_info.compressed_size = data.len() as u64;
header.data_info.uncompressed_size = uncompressed_size;
header.data_info.compression = compression_info;
let mut format = SnapshotFormat::new(header, data);
#[cfg(feature = "validation")]
if self.config.validation_enabled {
format = format.with_checksum(crate::format::ChecksumAlgorithm::Sha256)?;
}
self.write_snapshot_file(&mut format, output_path)?;
log::info!("Snapshot saved successfully");
Ok(())
}
fn compress_snapshot_data(
&self,
serialized: &[u8],
) -> SnapshotResult<(Vec<u8>, CompressionInfo)> {
if !self.config.compression_enabled
|| matches!(
self.config.compression_algorithm,
crate::CompressionAlgorithm::None
)
{
return Ok((
serialized.to_vec(),
CompressionInfo {
algorithm: format::CompressionAlgorithm::None,
level: 0,
parameters: std::collections::HashMap::new(),
},
));
}
let mut compression = CompressionEngine::new(Self::compression_config_for(&self.config));
let result = match self.config.compression_algorithm {
crate::CompressionAlgorithm::Auto => compression.compress(serialized)?,
crate::CompressionAlgorithm::Lz4 => compression.compress_with_algorithm(
serialized,
format::CompressionAlgorithm::Lz4 {
fast: self.config.compression_level <= 3,
},
)?,
crate::CompressionAlgorithm::Zstd => compression.compress_with_algorithm(
serialized,
format::CompressionAlgorithm::Zstd { dictionary: None },
)?,
crate::CompressionAlgorithm::None => unreachable!("handled before compression"),
};
Ok((result.data, result.info))
}
fn write_snapshot_file<P: AsRef<Path>>(
&self,
format: &mut SnapshotFormat,
output_path: P,
) -> SnapshotResult<()> {
use std::io::Write;
let mut file = std::fs::File::create(output_path)?;
let (header_data, header_size) = Self::encode_header_with_offset(&mut format.header)?;
file.write_all(&header_size.to_le_bytes())?;
file.write_all(&header_data)?;
file.write_all(&format.data)?;
if let Some(checksum) = &format.checksum {
file.write_all(checksum)?;
}
file.sync_all()?;
Ok(())
}
fn encode_header_with_offset(header: &mut SnapshotHeader) -> SnapshotResult<(Vec<u8>, u32)> {
const MAX_ITER: usize = 4;
let mut last_size = None;
for _ in 0..MAX_ITER {
let header_data = bincode::serialize(header)?;
let header_size = header_data.len() as u32;
let desired_offset = 4 + header_size as u64;
if header.data_info.data_offset == desired_offset {
return Ok((header_data, header_size));
}
header.data_info.data_offset = desired_offset;
last_size = Some(header_size);
}
Err(SnapshotError::Configuration {
message: format!(
"Snapshot header failed to stabilize data_offset after {MAX_ITER} attempts (last observed size: {:?})",
last_size
),
})
}
fn start_build(&self) {
{
let mut stats = self.stats.write();
stats.start_time = Some(Instant::now());
}
log::info!("Starting snapshot build");
if let Some(ref progress) = self.progress {
progress.set_message("Initializing...");
}
}
fn finish_build(&self) {
if let Some(ref progress) = self.progress {
progress.finish_with_message("Snapshot build completed!");
}
let stats = self.stats.read();
if let Some(start_time) = stats.start_time {
let total_time = start_time.elapsed();
log::info!("Snapshot build completed in {total_time:?}");
}
}
fn update_progress(&self, current: u64, total: u64, message: &str) {
if let Some(ref progress) = self.progress {
progress.set_position((current * 100) / total);
progress.set_message(message.to_string());
}
}
fn estimate_peak_memory_usage(&self) -> usize {
std::mem::size_of::<Snapshot>() * 2 }
pub fn stats(&self) -> BuildStats {
let stats = self.stats.read();
BuildStats {
start_time: stats.start_time,
phase_times: stats.phase_times.clone(),
memory_usage: stats.memory_usage.clone(),
items_processed: stats.items_processed.clone(),
errors: stats.errors.clone(),
warnings: stats.warnings.clone(),
}
}
}
fn cap_optimization_level(
inferred: OptimizationLevel,
max_level: OptimizationLevel,
) -> OptimizationLevel {
if optimization_rank(inferred) > optimization_rank(max_level) {
max_level
} else {
inferred
}
}
fn optimization_rank(level: OptimizationLevel) -> u8 {
match level {
OptimizationLevel::None => 0,
OptimizationLevel::Basic => 1,
OptimizationLevel::Aggressive => 2,
OptimizationLevel::MaxPerformance => 3,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_snapshot_builder_creation() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
let stats = builder.stats();
assert!(stats.start_time.is_none());
assert!(stats.errors.is_empty());
}
#[test]
fn test_builtin_analysis() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
fn test_builtin(_args: &[runmat_builtins::Value]) -> runmat_builtins::BuiltinFuture {
Box::pin(async { Ok(runmat_builtins::Value::Num(0.0)) })
}
let builtin = runmat_builtins::BuiltinFunction::new(
"matmul",
"Test builtin function",
"Category",
"",
"",
vec![],
runmat_builtins::Type::Num,
None,
test_builtin,
&[],
false,
false,
);
let metadata = builder.analyze_builtin_function(&builtin).unwrap();
assert_eq!(metadata.name, "matmul");
assert!(matches!(metadata.category, BuiltinCategory::LinearAlgebra));
assert!(matches!(
metadata.complexity,
ComputationalComplexity::Cubic
));
}
#[test]
fn test_category_inference() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
assert!(matches!(
builder.infer_builtin_category("sin"),
BuiltinCategory::Trigonometric
));
assert!(matches!(
builder.infer_builtin_category("matmul"),
BuiltinCategory::LinearAlgebra
));
assert!(matches!(
builder.infer_builtin_category("max"),
BuiltinCategory::Comparison
));
}
#[test]
fn test_complexity_inference() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
assert!(matches!(
builder.infer_computational_complexity("matmul"),
ComputationalComplexity::Cubic
));
assert!(matches!(
builder.infer_computational_complexity("dot"),
ComputationalComplexity::Linear
));
assert!(matches!(
builder.infer_computational_complexity("abs"),
ComputationalComplexity::Constant
));
}
#[test]
fn test_optimization_level_respects_config_cap() {
let config = SnapshotConfig {
max_optimization_level: OptimizationLevel::Basic,
..SnapshotConfig::default()
};
let builder = SnapshotBuilder::new(config);
assert_eq!(
builder.infer_optimization_level("matmul", &BuiltinCategory::LinearAlgebra),
OptimizationLevel::Basic
);
assert_eq!(
builder.infer_optimization_level("sin", &BuiltinCategory::Trigonometric),
OptimizationLevel::Basic
);
assert_eq!(
builder.infer_optimization_level("mean", &BuiltinCategory::Statistics),
OptimizationLevel::Basic
);
assert_eq!(
builder.infer_optimization_level("disp", &BuiltinCategory::Utility),
OptimizationLevel::None
);
}
#[test]
fn test_empty_snapshot_creation() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
let snapshot = builder.create_empty_snapshot();
assert!(snapshot.builtins.functions.is_empty());
assert!(snapshot.hir_cache.functions.is_empty());
assert!(snapshot.bytecode_cache.stdlib_bytecode.is_empty());
}
#[test]
fn test_gc_performance_profile() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
let profile = builder.create_gc_performance_profile("low-latency");
assert!(profile.average_collection_time < Duration::from_millis(1));
assert!(profile.memory_overhead < 0.2);
}
#[test]
fn test_build_phases() {
let phases = SnapshotBuilder::test_all_phases();
assert_eq!(phases.len(), 10);
for phase in &phases {
let requirements = SnapshotBuilder::analyze_phase_requirements(phase);
assert!(!requirements.is_empty());
match phase {
BuildPhase::Compression => assert!(phase.needs_compression()),
BuildPhase::Validation => assert!(phase.needs_validation()),
BuildPhase::Serialization => assert!(phase.involves_serialization()),
BuildPhase::Finalization => assert!(phase.involves_serialization()),
_ => {
assert!(!phase.needs_compression());
assert!(!phase.needs_validation());
}
}
}
}
#[test]
fn test_compression_engine_access() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
let _engine = builder.compression_engine();
}
#[cfg(feature = "validation")]
#[test]
fn test_validator_access() {
let config = SnapshotConfig::default();
let builder = SnapshotBuilder::new(config);
let _validator = builder.validator();
}
}