#[derive(Debug, Clone)]
pub struct WasmMemoryConfig {
pub initial_pages: u32,
pub maximum_pages: Option<u32>,
pub shared: bool,
pub growth_strategy: MemoryGrowthStrategy,
pub alignment: MemoryAlignment,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MemoryGrowthStrategy {
Fixed,
OnDemand,
PreAllocated,
Streaming,
pub struct MemoryAlignment {
pub data_alignment: u32,
pub function_alignment: u32,
pub simd_alignment: u32,
pub struct ProgressiveLoadingConfig {
pub enable: bool,
pub strategy: LoadingStrategy,
pub chunk_size: usize,
pub preloading: PreloadingConfig,
pub streaming: bool,
pub enum LoadingStrategy {
Eager,
Lazy,
Chunked,
pub struct PreloadingConfig {
pub percentage: f64,
pub on_idle: bool,
pub on_interaction: bool,
pub struct CachingConfig {
pub strategy: CacheStrategy,
pub storage: CacheStorage,
pub ttl_seconds: Option<u64>,
pub versioning: VersioningStrategy,
pub enum CacheStrategy {
LRU,
LFU,
FIFO,
TTL,
Custom,
pub enum CacheStorage {
CacheAPI,
IndexedDB,
LocalStorage,
SessionStorage,
Memory,
pub enum VersioningStrategy {
Hash,
Timestamp,
Semantic,
Custom(String),
pub struct ParallelConfig {
pub web_workers: bool,
pub max_workers: Option<usize>,
pub shared_memory: bool,
pub work_stealing: bool,
pub struct WasmMemoryExport {
pub name: String,
pub config: WasmMemoryConfig,
pub struct WasmMemoryImport {
pub module: String,
impl Default for WasmMemoryConfig {
fn default() -> Self {
Self {
initial_pages: 256, maximum_pages: Some(1024), shared: false,
growth_strategy: MemoryGrowthStrategy::OnDemand,
alignment: MemoryAlignment::default(),
}
}
impl Default for MemoryAlignment {
data_alignment: 8, function_alignment: 16, simd_alignment: 16, impl Default for ProgressiveLoadingConfig {
enable: true,
strategy: LoadingStrategy::Lazy,
chunk_size: 1024 * 1024, preloading: PreloadingConfig::default(),
streaming: true,
impl Default for PreloadingConfig {
percentage: 0.1, on_idle: true,
on_interaction: false,
impl Default for CachingConfig {
strategy: CacheStrategy::LRU,
storage: CacheStorage::CacheAPI,
ttl_seconds: Some(3600), versioning: VersioningStrategy::Hash,
impl Default for ParallelConfig {
web_workers: true,
max_workers: Some(4),
shared_memory: false,
work_stealing: false,
impl WasmMemoryConfig {
pub fn new(_initial_pages: u32, maximumpages: Option<u32>) -> Self {
initial_pages,
maximum_pages,
pub fn small() -> Self {
initial_pages: 64, maximum_pages: Some(256), growth_strategy: MemoryGrowthStrategy::Fixed,
pub fn large() -> Self {
initial_pages: 512, maximum_pages: Some(4096), growth_strategy: MemoryGrowthStrategy::Streaming,
alignment: MemoryAlignment::high_performance(),
pub fn multithreaded() -> Self {
maximum_pages: Some(2048), shared: true,
growth_strategy: MemoryGrowthStrategy::PreAllocated,
pub fn initial_size_bytes(&self) -> usize {
self._initial_pages as usize * 65536 pub fn max_size_bytes(&self) -> Option<usize> {
self.maximum_pages.map(|pages| pages as usize * 65536)
pub fn supports_growth(&self) -> bool {
self.growth_strategy != MemoryGrowthStrategy::Fixed
pub fn is_large_model_config(&self) -> bool {
self.initial_size_bytes() >= 32 * 1024 * 1024 impl MemoryAlignment {
pub fn high_performance() -> Self {
data_alignment: 32, function_alignment: 32, simd_alignment: 32, pub fn compact() -> Self {
data_alignment: 4, function_alignment: 8, simd_alignment: 16, pub fn is_simd_compatible(&self) -> bool {
self.simd_alignment >= 16
pub fn is_cache_optimized(&self) -> bool {
self.data_alignment >= 32 && self.function_alignment >= 32
impl ProgressiveLoadingConfig {
pub fn fast_start() -> Self {
chunk_size: 512 * 1024, preloading: PreloadingConfig {
enable: true,
percentage: 0.05, on_idle: true,
on_interaction: true,
},
pub fn low_bandwidth() -> Self {
strategy: LoadingStrategy::Chunked,
chunk_size: 128 * 1024, enable: false,
percentage: 0.0,
on_idle: false,
on_interaction: false,
pub fn has_preloading(&self) -> bool {
self.preloading.enable
pub fn preload_memory_overhead(&self, totalsize: usize) -> usize {
if self.has_preloading() {
(total_size as f64 * self.preloading.percentage) as usize
} else {
0
impl CachingConfig {
pub fn aggressive() -> Self {
storage: CacheStorage::IndexedDB,
ttl_seconds: Some(7 * 24 * 3600), pub fn minimal() -> Self {
strategy: CacheStrategy::TTL,
storage: CacheStorage::Memory,
ttl_seconds: Some(300), versioning: VersioningStrategy::Timestamp,
pub fn uses_persistent_storage(&self) -> bool {
matches!(
self.storage,
CacheStorage::CacheAPI | CacheStorage::IndexedDB | CacheStorage::LocalStorage
)
pub fn cache_lifetime(&self) -> Option<u64> {
self.ttl_seconds
impl ParallelConfig {
pub fn max_parallel() -> Self {
max_workers: Some(navigator_hardware_concurrency().unwrap_or(8)),
shared_memory: true,
work_stealing: true,
pub fn single_threaded() -> Self {
web_workers: false,
max_workers: Some(1),
pub fn effective_workers(&self) -> usize {
if self.web_workers {
self.max_workers.unwrap_or(1)
1
pub fn supports_multithreading(&self) -> bool {
self.web_workers && self.effective_workers() > 1
#[allow(dead_code)]
fn navigator_hardware_concurrency() -> Option<usize> {
Some(4)
pub struct MemoryManager {
config: WasmMemoryConfig,
progressive_config: ProgressiveLoadingConfig,
cache_config: CachingConfig,
parallel_config: ParallelConfig,
impl MemoryManager {
pub fn new(
config: WasmMemoryConfig,
progressive_config: ProgressiveLoadingConfig,
cache_config: CachingConfig,
parallel_config: ParallelConfig,
) -> Self {
config,
progressive_config,
cache_config,
parallel_config,
pub fn performance_optimized() -> Self {
config: WasmMemoryConfig::large(),
progressive_config: ProgressiveLoadingConfig::fast_start(),
cache_config: CachingConfig::aggressive(),
parallel_config: ParallelConfig::max_parallel(),
pub fn resource_constrained() -> Self {
config: WasmMemoryConfig::small(),
progressive_config: ProgressiveLoadingConfig::low_bandwidth(),
cache_config: CachingConfig::minimal(),
parallel_config: ParallelConfig::single_threaded(),
pub fn memory_config(&self) -> &WasmMemoryConfig {
&self.config
pub fn progressive_config(&self) -> &ProgressiveLoadingConfig {
&self.progressive_config
pub fn cache_config(&self) -> &CachingConfig {
&self.cache_config
pub fn parallel_config(&self) -> &ParallelConfig {
&self.parallel_config
pub fn calculate_memory_requirements(&self, modelsize: usize) -> WasmMemoryRequirements {
let base_memory = self._config.initial_size_bytes();
let model_memory = model_size;
let cache_overhead = if self.cache_config.enable {
model_size / 10 };
let preload_overhead = self.progressive_config.preload_memory_overhead(model_size);
let worker_overhead = if self.parallel_config.supports_multithreading() {
model_size * self.parallel_config.effective_workers() / 4 WasmMemoryRequirements {
base_memory,
model_memory,
cache_overhead,
preload_overhead,
worker_overhead,
total: base_memory + model_memory + cache_overhead + preload_overhead + worker_overhead,
pub fn is_suitable_for_model(&self, modelsize: usize) -> bool {
let requirements = self.calculate_memory_requirements(model_size);
if let Some(max_size) = self.config.max_size_bytes() {
requirements.total <= max_size
true pub fn recommended_chunk_size(&self, modelsize: usize) -> usize {
let base_chunk = self.progressive_config.chunk_size;
if model_size < 1024 * 1024 {
base_chunk / 4
} else if model_size > 100 * 1024 * 1024 {
base_chunk * 2
base_chunk
pub struct WasmMemoryRequirements {
pub base_memory: usize,
pub model_memory: usize,
pub cache_overhead: usize,
pub preload_overhead: usize,
pub worker_overhead: usize,
pub total: usize,
impl WasmMemoryRequirements {
pub fn breakdown_percentages(&self) -> MemoryBreakdown {
let total_f = self.total as f64;
MemoryBreakdown {
base_percent: (self.base_memory as f64 / total_f) * 100.0,
model_percent: (self.model_memory as f64 / total_f) * 100.0,
cache_percent: (self.cache_overhead as f64 / total_f) * 100.0,
preload_percent: (self.preload_overhead as f64 / total_f) * 100.0,
worker_percent: (self.worker_overhead as f64 / total_f) * 100.0,
pub fn format_size(bytes: usize) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
if _bytes == 0 {
return "0 B".to_string();
let mut size = _bytes as f64;
let mut unit_index = 0;
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
if unit_index == 0 {
format!("{} {}", bytes, UNITS[unit_index])
format!("{:.1} {}", size, UNITS[unit_index])
/// Get formatted total memory requirement
pub fn total_formatted(&self) -> String {
Self::format_size(self.total)
/// Memory usage breakdown in percentages
pub struct MemoryBreakdown {
/// Percentage of memory for base operations
pub base_percent: f64,
/// Percentage of memory for model storage
pub model_percent: f64,
/// Percentage of memory for caching
pub cache_percent: f64,
/// Percentage of memory for preloading
pub preload_percent: f64,
/// Percentage of memory for worker threads
pub worker_percent: f64,
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wasm_memory_config_default() {
let config = WasmMemoryConfig::default();
assert_eq!(config.initial_pages, 256);
assert_eq!(config.maximum_pages, Some(1024));
assert!(!config.shared);
assert_eq!(config.growth_strategy, MemoryGrowthStrategy::OnDemand);
fn test_memory_config_sizes() {
let config = WasmMemoryConfig::new(128, Some(512));
assert_eq!(config.initial_size_bytes(), 128 * 65536);
assert_eq!(config.max_size_bytes(), Some(512 * 65536));
assert!(config.supports_growth());
fn test_memory_config_presets() {
let small = WasmMemoryConfig::small();
assert_eq!(small.initial_pages, 64);
assert_eq!(small.growth_strategy, MemoryGrowthStrategy::Fixed);
assert!(!small.supports_growth());
let large = WasmMemoryConfig::large();
assert_eq!(large.initial_pages, 512);
assert_eq!(large.growth_strategy, MemoryGrowthStrategy::Streaming);
assert!(large.is_large_model_config());
let mt = WasmMemoryConfig::multithreaded();
assert!(mt.shared);
assert_eq!(mt.growth_strategy, MemoryGrowthStrategy::PreAllocated);
fn test_memory_alignment() {
let default_align = MemoryAlignment::default();
assert!(default_align.is_simd_compatible());
assert!(!default_align.is_cache_optimized());
let perf_align = MemoryAlignment::high_performance();
assert!(perf_align.is_simd_compatible());
assert!(perf_align.is_cache_optimized());
let compact_align = MemoryAlignment::compact();
assert!(compact_align.is_simd_compatible());
assert!(!compact_align.is_cache_optimized());
fn test_progressive_loading_config() {
let config = ProgressiveLoadingConfig::default();
assert!(config.enable);
assert!(config.has_preloading());
let fast = ProgressiveLoadingConfig::fast_start();
assert_eq!(fast.chunk_size, 512 * 1024);
assert!(fast.preloading.on_interaction);
let low_bw = ProgressiveLoadingConfig::low_bandwidth();
assert_eq!(low_bw.chunk_size, 128 * 1024);
assert!(!low_bw.has_preloading());
fn test_caching_config() {
let default_cache = CachingConfig::default();
assert!(default_cache.enable);
assert!(default_cache.uses_persistent_storage());
assert_eq!(default_cache.cache_lifetime(), Some(3600));
let aggressive = CachingConfig::aggressive();
assert_eq!(aggressive.strategy, CacheStrategy::LRU);
assert_eq!(aggressive.cache_lifetime(), Some(7 * 24 * 3600));
let minimal = CachingConfig::minimal();
assert_eq!(minimal.strategy, CacheStrategy::TTL);
assert!(!minimal.uses_persistent_storage());
fn test_parallel_config() {
let default_parallel = ParallelConfig::default();
assert!(default_parallel.web_workers);
assert!(default_parallel.supports_multithreading());
let max_parallel = ParallelConfig::max_parallel();
assert!(max_parallel.work_stealing);
assert!(max_parallel.shared_memory);
let single = ParallelConfig::single_threaded();
assert!(!single.web_workers);
assert!(!single.supports_multithreading());
assert_eq!(single.effective_workers(), 1);
fn test_memory_manager() {
let manager = MemoryManager::performance_optimized();
assert!(manager.memory_config().is_large_model_config());
assert!(manager.parallel_config().supports_multithreading());
let constrained = MemoryManager::resource_constrained();
assert!(!constrained.memory_config().is_large_model_config());
assert!(!constrained.parallel_config().supports_multithreading());
fn test_memory_requirements_calculation() {
let model_size = 10 * 1024 * 1024; // 10MB model
let requirements = manager.calculate_memory_requirements(model_size);
assert!(requirements.total > model_size);
assert!(requirements.model_memory == model_size);
let breakdown = requirements.breakdown_percentages();
assert!(
(breakdown.base_percent
+ breakdown.model_percent
+ breakdown.cache_percent
+ breakdown.preload_percent
+ breakdown.worker_percent
- 100.0)
.abs()
< 0.1
);
fn test_memory_requirements_formatting() {
assert_eq!(WasmMemoryRequirements::format_size(0), "0 B");
assert_eq!(WasmMemoryRequirements::format_size(1024), "1.0 KB");
assert_eq!(WasmMemoryRequirements::format_size(1024 * 1024), "1.0 MB");
assert_eq!(WasmMemoryRequirements::format_size(1536), "1.5 KB");
fn test_recommended_chunk_size() {
// Small model should get smaller chunks
let small_chunk = manager.recommended_chunk_size(512 * 1024);
assert!(small_chunk < manager.progressive_config().chunk_size);
// Large model should get larger chunks
let large_chunk = manager.recommended_chunk_size(200 * 1024 * 1024);
assert!(large_chunk > manager.progressive_config().chunk_size);
// Medium model should get default chunks
let medium_chunk = manager.recommended_chunk_size(10 * 1024 * 1024);
assert_eq!(medium_chunk, manager.progressive_config().chunk_size);