metrale_gpu_runtime/buffers/
moe_fp8_scratch.rs1use super::BufferArena;
16use crate::gpu::DevicePtr;
17use metrale_config::ModelConfig;
18
19pub(super) struct Layout {
20 pub(super) scale: usize,
21 pub(super) worklist: usize,
22 pub(super) counter: usize,
23 pub(super) small_worklist: usize,
24 pub(super) small_counter: usize,
25 pub(super) small_bytes: usize,
26 pub(super) bytes: usize,
27}
28
29impl Layout {
30 pub(super) fn new(c: &ModelConfig, rows: usize) -> Self {
31 if c.num_experts == 0 {
32 return Self {
33 scale: 0,
34 worklist: 0,
35 counter: 0,
36 small_worklist: 0,
37 small_counter: 0,
38 small_bytes: 0,
39 bytes: 0,
40 };
41 }
42 let expanded = rows * c.num_experts_per_tok;
43 let shared_k = c.hidden_size.max(c.shared_expert_intermediate_size);
44 let activation = (rows * shared_k).max(expanded * c.moe_intermediate_size);
45 let scales = (rows * shared_k.div_ceil(128))
46 .max(expanded * c.moe_intermediate_size.div_ceil(128))
47 * 4;
48 let items = (expanded.div_ceil(128) + c.num_experts + 1)
51 * c.hidden_size.max(c.moe_intermediate_size).div_ceil(64);
52 let align = |n: usize| n.div_ceil(256) * 256;
53 let scale = align(activation);
54 let worklist = scale + align(scales);
55 let counter = worklist + align(items * 8);
56 let small_bytes = if c.num_experts == 256 {
59 small_list_bytes(c.num_experts, c.hidden_size.max(c.moe_intermediate_size))
60 .expect("adaptive FP8 small worklist size overflow")
61 } else {
62 0
63 };
64 let small_worklist = if small_bytes > 0 {
65 align(counter + 4)
66 } else {
67 0
68 };
69 let small_counter = small_worklist + align(small_bytes);
70 Self {
71 scale,
72 worklist,
73 counter,
74 small_worklist,
75 small_counter,
76 small_bytes,
77 bytes: if small_bytes > 0 {
78 small_counter + 4
79 } else {
80 counter + 4
81 },
82 }
83 }
84}
85
86fn small_list_bytes(experts: usize, width: usize) -> Option<usize> {
87 experts.checked_mul(width.div_ceil(64))?.checked_mul(8)
88}
89
90pub struct MoeFp8Scratch {
92 pub activation: DevicePtr,
93 pub scales: DevicePtr,
94 pub worklist: DevicePtr,
95 pub total_tiles: DevicePtr,
96 pub worklist_bytes: usize,
97 pub small_worklist: DevicePtr,
98 pub small_total_tiles: DevicePtr,
99 pub small_worklist_bytes: usize,
100}
101
102impl BufferArena {
103 pub fn moe_fp8_scratch(&self, c: &ModelConfig, rows: usize) -> anyhow::Result<MoeFp8Scratch> {
109 let l = Layout::new(c, rows);
110 anyhow::ensure!(
111 rows <= self.max_batch_tokens
112 && l.bytes > 0
113 && l.bytes <= self.sizes.moe_fp8_scratch
114 && self.moe_fp8_scratch != DevicePtr::NULL,
115 "FP8 MoE scratch exceeds arena: rows={rows}, requested={} bytes, capacity={} bytes",
116 l.bytes,
117 self.sizes.moe_fp8_scratch
118 );
119 Ok(MoeFp8Scratch {
120 activation: self.moe_fp8_scratch,
121 scales: self.moe_fp8_scratch.offset(l.scale),
122 worklist: self.moe_fp8_scratch.offset(l.worklist),
123 total_tiles: self.moe_fp8_scratch.offset(l.counter),
124 worklist_bytes: l.counter - l.worklist,
125 small_worklist: if l.small_bytes > 0 {
126 self.moe_fp8_scratch.offset(l.small_worklist)
127 } else {
128 DevicePtr::NULL
129 },
130 small_total_tiles: if l.small_bytes > 0 {
131 self.moe_fp8_scratch.offset(l.small_counter)
132 } else {
133 DevicePtr::NULL
134 },
135 small_worklist_bytes: l.small_bytes,
136 })
137 }
138}
139
140#[cfg(test)]
141#[path = "moe_fp8_scratch_tests.rs"]
142mod tests;