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: &Path) -> Result<()> {
let mut store = match map_file(path) {
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| ModelError::LoadFailed {
path: path.to_path_buf(),
source: Box::new(e),
})?;
Ok(())
}
fn map_file(path: &Path) -> std::io::Result<Bytes> {
let file = File::open(path)?;
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}");
}
#[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());
}
}