Streaming Compilation

Status: Verified | Idempotent: Yes | Coverage: 95%+

Run Command

cargo run --example wasm_streaming_compilation

Code

//! # Recipe: WASM Streaming Compilation
//!
//! Contract: contracts/recipe-iiur-v1.yaml
//! **Category**: WASM/Browser
//! **Isolation Level**: Full
//! **Idempotency**: Guaranteed
//! **Dependencies**: None (default features)
//!
//! ## QA Checklist
//! 1. [x] `cargo run` succeeds (Exit Code 0)
//! 2. [x] `cargo test` passes
//! 3. [x] Deterministic output (Verified)
//! 4. [x] No temp files leaked
//! 5. [x] Memory usage stable
//! 6. [x] WASM compatible (Verified)
//! 7. [x] Clippy clean
//! 8. [x] Rustfmt standard
//! 9. [x] No `unwrap()` in logic
//! 10. [x] Proptests pass (100+ cases)
//!
//! ## Learning Objective
//! Stream-compile WASM module while downloading.
//!
//! ## Run Command
//! ```bash
//! cargo run --example wasm_streaming_compilation
//! ```
//!
//!
//! ## Format Variants
//! ```bash
//! apr run model.apr          # APR native format
//! apr run model.gguf         # GGUF (llama.cpp compatible)
//! apr run model.safetensors  # SafeTensors (HuggingFace)
//! ```
//! ## References
//! - Haas, A. et al. (2017). *Bringing the Web up to Speed with WebAssembly*. PLDI. DOI: 10.1145/3062341.3062363

use apr_cookbook::prelude::*;
use serde::{Deserialize, Serialize};

fn main() -> Result<()> {
    let mut ctx = RecipeContext::new("wasm_streaming_compilation")?;

    println!("=== Recipe: {} ===", ctx.name());
    println!("WASM streaming compilation simulation");
    println!();

    // WASM module info
    let module = WasmModule {
        name: "model-inference".to_string(),
        size_kb: 500,
        sections: vec![
            WasmSection {
                name: "type".to_string(),
                size_kb: 10,
            },
            WasmSection {
                name: "import".to_string(),
                size_kb: 20,
            },
            WasmSection {
                name: "function".to_string(),
                size_kb: 50,
            },
            WasmSection {
                name: "table".to_string(),
                size_kb: 5,
            },
            WasmSection {
                name: "memory".to_string(),
                size_kb: 10,
            },
            WasmSection {
                name: "global".to_string(),
                size_kb: 5,
            },
            WasmSection {
                name: "export".to_string(),
                size_kb: 10,
            },
            WasmSection {
                name: "code".to_string(),
                size_kb: 350,
            },
            WasmSection {
                name: "data".to_string(),
                size_kb: 40,
            },
        ],
    };

    ctx.record_metric("module_size_kb", i64::from(module.size_kb));
    ctx.record_metric("section_count", module.sections.len() as i64);

    println!("WASM Module: {}", module.name);
    println!("Total size: {}KB", module.size_kb);
    println!();
    println!("Sections:");
    for section in &module.sections {
        println!("  {}: {}KB", section.name, section.size_kb);
    }
    println!();

    // Compare compilation strategies
    println!("Compilation Strategy Comparison:");
    println!("{:-<65}", "");
    println!(
        "{:<20} {:>12} {:>12} {:>15}",
        "Strategy", "Download", "Compile", "Time-to-Ready"
    );
    println!("{:-<65}", "");

    // Synchronous compilation
    let sync_result = simulate_sync_compilation(&module);
    println!(
        "{:<20} {:>10}ms {:>10}ms {:>13}ms",
        "Synchronous", sync_result.download_ms, sync_result.compile_ms, sync_result.total_ms
    );

    // Streaming compilation
    let stream_result = simulate_streaming_compilation(&module);
    println!(
        "{:<20} {:>10}ms {:>10}ms {:>13}ms",
        "Streaming", stream_result.download_ms, stream_result.compile_ms, stream_result.total_ms
    );

    // Cached compilation
    let cached_result = simulate_cached_compilation(&module);
    println!(
        "{:<20} {:>10}ms {:>10}ms {:>13}ms",
        "Cached", cached_result.download_ms, cached_result.compile_ms, cached_result.total_ms
    );

    println!("{:-<65}", "");

    // Calculate improvements
    let stream_improvement = ((f64::from(sync_result.total_ms)
        - f64::from(stream_result.total_ms))
        / f64::from(sync_result.total_ms))
        * 100.0;
    let cache_improvement = ((f64::from(sync_result.total_ms) - f64::from(cached_result.total_ms))
        / f64::from(sync_result.total_ms))
        * 100.0;

    ctx.record_float_metric("streaming_improvement_pct", stream_improvement);
    ctx.record_float_metric("cache_improvement_pct", cache_improvement);

    println!();
    println!("Improvement over synchronous:");
    println!("  Streaming: {:.1}% faster", stream_improvement);
    println!("  Cached: {:.1}% faster", cache_improvement);

    // Browser compatibility
    let compat = check_browser_compatibility();
    println!();
    println!("Browser Streaming Support:");
    for (browser, supported) in &compat {
        let status = if *supported { "✓" } else { "✗" };
        println!("  {} {}", status, browser);
    }

    // Save results
    let results_path = ctx.path("streaming_results.json");
    save_results(&results_path, &[sync_result, stream_result, cached_result])?;
    println!();
    println!("Results saved to: {:?}", results_path);

    Ok(())
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct WasmSection {
    name: String,
    size_kb: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct WasmModule {
    name: String,
    size_kb: u32,
    sections: Vec<WasmSection>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct CompilationResult {
    strategy: String,
    download_ms: u32,
    compile_ms: u32,
    total_ms: u32,
}

fn simulate_sync_compilation(module: &WasmModule) -> CompilationResult {
    // Synchronous: download first, then compile
    let download_ms = module.size_kb; // 1ms per KB
    let compile_ms = module.size_kb / 2; // 0.5ms per KB for compilation

    CompilationResult {
        strategy: "synchronous".to_string(),
        download_ms,
        compile_ms,
        total_ms: download_ms + compile_ms,
    }
}

fn simulate_streaming_compilation(module: &WasmModule) -> CompilationResult {
    // Streaming: compile while downloading (parallel)
    let download_ms = module.size_kb; // 1ms per KB
    let compile_ms = module.size_kb / 2; // 0.5ms per KB

    // Total is max of download and compile (overlapped)
    // Plus some overhead for streaming
    let overhead = 20u32; // Streaming overhead
    let total_ms = download_ms.max(compile_ms) + overhead;

    CompilationResult {
        strategy: "streaming".to_string(),
        download_ms,
        compile_ms,
        total_ms,
    }
}

fn simulate_cached_compilation(module: &WasmModule) -> CompilationResult {
    // Cached: no download, minimal compile (validation only)
    let download_ms = 0; // From cache
    let compile_ms = module.size_kb / 20; // Just validation, 20x faster

    CompilationResult {
        strategy: "cached".to_string(),
        download_ms,
        compile_ms,
        total_ms: download_ms + compile_ms,
    }
}

fn check_browser_compatibility() -> Vec<(String, bool)> {
    vec![
        ("Chrome 61+".to_string(), true),
        ("Firefox 58+".to_string(), true),
        ("Safari 15+".to_string(), true),
        ("Edge 79+".to_string(), true),
        ("Opera 48+".to_string(), true),
        ("IE 11".to_string(), false),
    ]
}

fn save_results(path: &std::path::Path, results: &[CompilationResult]) -> Result<()> {
    let json = serde_json::to_string_pretty(results)
        .map_err(|e| CookbookError::Serialization(e.to_string()))?;
    std::fs::write(path, json)?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    fn test_module() -> WasmModule {
        WasmModule {
            name: "test".to_string(),
            size_kb: 100,
            sections: vec![
                WasmSection {
                    name: "code".to_string(),
                    size_kb: 80,
                },
                WasmSection {
                    name: "data".to_string(),
                    size_kb: 20,
                },
            ],
        }
    }

    #[test]
    fn test_sync_compilation() {
        let module = test_module();
        let result = simulate_sync_compilation(&module);

        assert_eq!(result.strategy, "synchronous");
        assert_eq!(result.total_ms, result.download_ms + result.compile_ms);
    }

    #[test]
    fn test_streaming_faster_than_sync() {
        let module = test_module();
        let sync = simulate_sync_compilation(&module);
        let stream = simulate_streaming_compilation(&module);

        assert!(stream.total_ms < sync.total_ms);
    }

    #[test]
    fn test_cached_fastest() {
        let module = test_module();
        let sync = simulate_sync_compilation(&module);
        let stream = simulate_streaming_compilation(&module);
        let cached = simulate_cached_compilation(&module);

        assert!(cached.total_ms < stream.total_ms);
        assert!(cached.total_ms < sync.total_ms);
    }

    #[test]
    fn test_cached_no_download() {
        let module = test_module();
        let cached = simulate_cached_compilation(&module);

        assert_eq!(cached.download_ms, 0);
    }

    #[test]
    fn test_browser_compatibility() {
        let compat = check_browser_compatibility();

        assert!(!compat.is_empty());
        // Modern browsers should support streaming
        let chrome_support = compat.iter().find(|(b, _)| b.contains("Chrome"));
        assert!(chrome_support.is_some());
        assert!(chrome_support.unwrap().1);
    }

    #[test]
    fn test_save_results() {
        let ctx = RecipeContext::new("test_streaming_save").unwrap();
        let path = ctx.path("results.json");

        let results = vec![CompilationResult {
            strategy: "test".to_string(),
            download_ms: 100,
            compile_ms: 50,
            total_ms: 150,
        }];

        save_results(&path, &results).unwrap();
        assert!(path.exists());
    }
}

#[cfg(test)]
mod proptests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        #![proptest_config(ProptestConfig::with_cases(100))]

        #[test]
        fn prop_streaming_faster(size_kb in 50u32..1000) {
            let module = WasmModule {
                name: "test".to_string(),
                size_kb,
                sections: vec![],
            };

            let sync = simulate_sync_compilation(&module);
            let stream = simulate_streaming_compilation(&module);

            prop_assert!(stream.total_ms < sync.total_ms);
        }

        #[test]
        fn prop_cached_no_download(size_kb in 50u32..1000) {
            let module = WasmModule {
                name: "test".to_string(),
                size_kb,
                sections: vec![],
            };

            let cached = simulate_cached_compilation(&module);
            prop_assert_eq!(cached.download_ms, 0);
        }

        #[test]
        fn prop_total_positive(size_kb in 1u32..500) {
            let module = WasmModule {
                name: "test".to_string(),
                size_kb,
                sections: vec![],
            };

            let sync = simulate_sync_compilation(&module);
            let stream = simulate_streaming_compilation(&module);
            let cached = simulate_cached_compilation(&module);

            prop_assert!(sync.total_ms > 0);
            prop_assert!(stream.total_ms > 0);
            prop_assert!(cached.total_ms <= sync.total_ms);
        }
    }
}