use std::fs::File;
use std::path::Path;
use burn::tensor::{AllocationProperty, Bytes};
use burn_store::{BurnpackStore, ModuleSnapshot};
use memmap2::Mmap;
use tracing::warn;
use crate::error::{ModelError, Result};
pub fn load_burnpack<M: ModuleSnapshot>(model: &mut M, path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
let failed = |e: Box<dyn std::error::Error + Send + Sync>| ModelError::LoadFailed {
path: path.to_path_buf(),
source: e,
};
let file = File::open(path).map_err(|e| failed(Box::new(e)))?;
let mut store = match map_file(&file) {
Ok(bytes) => BurnpackStore::from_bytes(Some(bytes)),
Err(e) => {
warn!(
"Could not memory-map {}: {e}; reading it into memory instead",
path.display()
);
BurnpackStore::from_file(path).auto_extension(false)
}
};
model
.load_from(&mut store)
.map_err(|e| failed(Box::new(e)))?;
Ok(())
}
fn map_file(file: &File) -> std::io::Result<Bytes> {
let mmap = unsafe { Mmap::map(file)? };
let shared = bytes::Bytes::from_owner(mmap);
Ok(Bytes::from_shared(shared, AllocationProperty::File))
}
#[cfg(test)]
mod tests {
use super::*;
use burn::nn::{Linear, LinearConfig};
use burn::tensor::Device;
fn linear() -> Linear {
LinearConfig::new(4, 3).init(&Device::default())
}
#[test]
fn round_trips_through_mmap() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("linear.bpk");
let original = linear();
let mut store = BurnpackStore::from_file(&path).auto_extension(false);
original.save_into(&mut store).unwrap();
let mut loaded = linear();
load_burnpack(&mut loaded, &path).unwrap();
assert_eq!(
loaded.weight.val().into_data(),
original.weight.val().into_data()
);
drop(store);
let _ = loaded.weight.val().sum().into_data();
}
#[test]
fn missing_file_is_an_error() {
let mut model = linear();
let err = load_burnpack(&mut model, Path::new("does-not-exist.bpk")).unwrap_err();
assert!(err.to_string().contains("does-not-exist.bpk"), "{err}");
let crate::error::FurnaceError::Model(ModelError::LoadFailed { source, .. }) = err else {
panic!("{err:?}");
};
assert!(
source.downcast_ref::<std::io::Error>().is_some(),
"{source:?}"
);
}
#[test]
fn malformed_file_is_an_error() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("garbage.bpk");
std::fs::write(&path, b"not a burnpack file").unwrap();
let mut model = linear();
assert!(load_burnpack(&mut model, &path).is_err());
}
}