metrale_model_layers/lora/
overlay_tables.rs1use anyhow::Result;
14use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
15
16use super::overlay_build::EmbedOverlay;
17
18pub struct TokenOverlaySet {
22 pub overlays: Vec<Option<EmbedOverlay>>,
25 pub embed_slot_map_table: DevicePtr,
28 pub embed_rows_table: DevicePtr,
30 pub embed_n_table: DevicePtr,
33 pub vocab: u32,
36 pub lmhead_rows_table: DevicePtr,
38 pub lmhead_ids_table: DevicePtr,
40 pub n_override_table: DevicePtr,
42 pub max_n_override: u32,
45}
46
47fn mk_u64(gpu: &dyn GpuBackend, tab: &[u64]) -> Result<DevicePtr> {
48 let bytes: Vec<u8> = tab.iter().flat_map(|p| p.to_le_bytes()).collect();
49 let d = gpu.alloc(bytes.len())?;
50 gpu.copy_h2d(&bytes, d)?;
51 Ok(d)
52}
53
54fn mk_u32(gpu: &dyn GpuBackend, tab: &[u32]) -> Result<DevicePtr> {
55 let bytes: Vec<u8> = tab.iter().flat_map(|p| p.to_le_bytes()).collect();
56 let d = gpu.alloc(bytes.len())?;
57 gpu.copy_h2d(&bytes, d)?;
58 Ok(d)
59}
60
61impl TokenOverlaySet {
62 pub fn from_slots(
67 gpu: &dyn GpuBackend,
68 overlays: Vec<Option<EmbedOverlay>>,
69 max_loras: usize,
70 tied: bool,
71 ) -> Result<Self> {
72 let mut slot_map_tab = vec![0u64; max_loras];
73 let mut embed_rows_tab = vec![0u64; max_loras];
74 let mut embed_n_tab = vec![0u32; max_loras];
75 let mut lmhead_rows_tab = vec![0u64; max_loras];
76 let mut lmhead_ids_tab = vec![0u64; max_loras];
77 let mut n_override_tab = vec![0u32; max_loras];
78 let mut max_n_override = 0u32;
79 let mut vocab = 0u32;
80
81 for (k, ov) in overlays.iter().enumerate() {
82 let Some(ov) = ov else { continue };
83 slot_map_tab[k] = ov.slot_map.0;
84 embed_rows_tab[k] = ov.rows.0;
85 embed_n_tab[k] = ov.n_override;
86 anyhow::ensure!(
88 vocab == 0 || vocab == ov.vocab,
89 "token-overlay: slot {k} was built against vocab {} but an \
90 earlier slot against {vocab}; refusing mixed-vocab overlay tables",
91 ov.vocab
92 );
93 vocab = ov.vocab;
94 let (rows, ids, n) = match (&ov.lmhead, tied) {
95 (Some(lm), _) => (lm.rows.0, lm.ids_dev.0, lm.n_override),
96 (None, true) => (ov.rows.0, ov.ids_dev.0, ov.n_override),
97 (None, false) => (0, 0, 0),
98 };
99 lmhead_rows_tab[k] = rows;
100 lmhead_ids_tab[k] = ids;
101 n_override_tab[k] = n;
102 max_n_override = max_n_override.max(n);
103 }
104
105 Ok(Self {
106 embed_slot_map_table: mk_u64(gpu, &slot_map_tab)?,
107 embed_rows_table: mk_u64(gpu, &embed_rows_tab)?,
108 embed_n_table: mk_u32(gpu, &embed_n_tab)?,
109 vocab,
110 lmhead_rows_table: mk_u64(gpu, &lmhead_rows_tab)?,
111 lmhead_ids_table: mk_u64(gpu, &lmhead_ids_tab)?,
112 n_override_table: mk_u32(gpu, &n_override_tab)?,
113 max_n_override,
114 overlays,
115 })
116 }
117
118 pub fn any_active(&self) -> bool {
120 self.overlays.iter().any(|o| o.is_some())
121 }
122}
123
124#[cfg(test)]
125#[path = "overlay_tables_tests.rs"]
126mod tests;