1use anyhow::{Context, Result, bail};
18use serde::{Deserialize, Serialize};
19
20use crate::expert::ExpertKey;
21use crate::expert::{ExpertLayout, ExpertRecordHeader, ExpertRecordSpec, Proj};
22
23#[derive(Clone, Copy, Debug)]
26pub struct ProjData<'a> {
27 pub packed: &'a [u8],
28 pub scale: &'a [u8],
29}
30
31#[derive(Clone, Copy, Debug)]
33pub struct ProjView<'a> {
34 pub packed: &'a [u8],
35 pub scale: &'a [u8],
36}
37
38pub fn pack_record(
46 spec: &ExpertRecordSpec,
47 stride: u64,
48 header: &ExpertRecordHeader,
49 projs: &[ProjData; 3],
50) -> Result<Vec<u8>> {
51 let stride = stride as usize;
52 if (spec.raw_bytes() as usize) > stride {
53 bail!(
54 "record stride {} smaller than raw record bytes {}",
55 stride,
56 spec.raw_bytes()
57 );
58 }
59 let mut buf = vec![0u8; stride];
60 let hdr = header.to_bytes();
61 buf[..hdr.len()].copy_from_slice(&hdr);
62
63 for p in Proj::ALL {
64 let pb = spec.proj_bytes(p);
65 let d = &projs[p as usize];
66 if d.packed.len() as u64 != pb.packed_bytes {
67 bail!(
68 "{:?} packed len {} != expected {}",
69 p,
70 d.packed.len(),
71 pb.packed_bytes
72 );
73 }
74 if d.scale.len() as u64 != pb.scale_bytes {
75 bail!(
76 "{:?} scale len {} != expected {}",
77 p,
78 d.scale.len(),
79 pb.scale_bytes
80 );
81 }
82 let po = spec.packed_off(p) as usize;
83 let so = spec.scale_off(p) as usize;
84 buf[po..po + d.packed.len()].copy_from_slice(d.packed);
85 buf[so..so + d.scale.len()].copy_from_slice(d.scale);
86 }
87 Ok(buf)
88}
89
90pub fn unpack_record<'a>(
94 spec: &ExpertRecordSpec,
95 buf: &'a [u8],
96) -> Result<(ExpertRecordHeader, [ProjView<'a>; 3])> {
97 if (buf.len() as u64) < spec.raw_bytes() {
98 bail!(
99 "record buffer {} smaller than raw record bytes {}",
100 buf.len(),
101 spec.raw_bytes()
102 );
103 }
104 let header = ExpertRecordHeader::from_bytes(buf)
105 .context("record header magic/version mismatch (wrong file or format version?)")?;
106 let mut views = [ProjView {
107 packed: &[],
108 scale: &[],
109 }; 3];
110 for p in Proj::ALL {
111 let pb = spec.proj_bytes(p);
112 let po = spec.packed_off(p) as usize;
113 let so = spec.scale_off(p) as usize;
114 views[p as usize] = ProjView {
115 packed: &buf[po..po + pb.packed_bytes as usize],
116 scale: &buf[so..so + pb.scale_bytes as usize],
117 };
118 }
119 Ok((header, views))
120}
121
122#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
126pub struct ExpertIndex {
127 pub version: u32,
129 pub num_moe_layers: u32,
130 pub num_experts: u32,
131 pub inter: u64,
132 pub hidden: u64,
133 pub group_size: u64,
134 pub sub_align: u64,
135 pub fs_block_size: u64,
136 pub record_stride: u64,
137 pub record_raw_bytes: u64,
138 pub file_template: String,
140 pub moe_layer_to_model_layer: Vec<u32>,
143}
144
145impl ExpertIndex {
146 pub const FILE_TEMPLATE: &'static str = "experts_{:05}.xpr";
147 pub const MANIFEST_NAME: &'static str = "manifest.json";
148
149 pub fn new(
150 inter: u64,
151 hidden: u64,
152 group_size: u64,
153 sub_align: u64,
154 fs_block_size: u64,
155 moe_layer_to_model_layer: Vec<u32>,
156 num_experts: u32,
157 ) -> Self {
158 let spec = ExpertRecordSpec::new(inter, hidden, group_size, sub_align);
159 let layout = ExpertLayout::from_spec(
160 moe_layer_to_model_layer.len() as u32,
161 num_experts,
162 &spec,
163 fs_block_size,
164 );
165 Self {
166 version: ExpertRecordHeader::VERSION,
167 num_moe_layers: moe_layer_to_model_layer.len() as u32,
168 num_experts,
169 inter,
170 hidden,
171 group_size,
172 sub_align,
173 fs_block_size,
174 record_stride: layout.record_stride,
175 record_raw_bytes: spec.raw_bytes(),
176 file_template: Self::FILE_TEMPLATE.to_string(),
177 moe_layer_to_model_layer,
178 }
179 }
180
181 #[cfg(unix)]
185 pub fn load(dir: &std::path::Path) -> Result<Self> {
186 let p = dir.join(Self::MANIFEST_NAME);
187 let json = std::fs::read_to_string(&p).with_context(|| format!("read {}", p.display()))?;
188 let index: ExpertIndex =
189 serde_json::from_str(&json).with_context(|| format!("parse {}", p.display()))?;
190 if index.version != ExpertRecordHeader::VERSION {
191 bail!(
192 "manifest version {} != supported {}",
193 index.version,
194 ExpertRecordHeader::VERSION
195 );
196 }
197 Ok(index)
198 }
199
200 pub fn spec(&self) -> ExpertRecordSpec {
201 ExpertRecordSpec::new(self.inter, self.hidden, self.group_size, self.sub_align)
202 }
203
204 pub fn layout(&self) -> ExpertLayout {
205 ExpertLayout::from_spec(
206 self.num_moe_layers,
207 self.num_experts,
208 &self.spec(),
209 self.fs_block_size,
210 )
211 }
212
213 pub fn file_name(&self, moe_layer: u32) -> String {
215 format!("experts_{moe_layer:05}.xpr")
218 }
219
220 pub fn total_bytes(&self) -> u64 {
222 (self.num_moe_layers as u64) * self.layout().bytes_per_layer()
223 }
224}
225
226pub use fs_impl::{ExpertFileReader, ExpertFileWriter};
227
228#[path = "expert_pack_fs.rs"]
229mod fs_impl;
230
231#[cfg(test)]
232#[path = "expert_pack_tests.rs"]
233mod tests;