1use anyhow::Result;
5use spark_runtime::gpu::{DevicePtr, GpuBackend, KernelArg};
6
7use super::super::quant_weights::QuantWeights;
8use super::{
9 LinearAttentionLayer, LinearAttentionScratch, LinearAttentionState, Qwen35ForwardConfig,
10 Qwen35Kernels,
11};
12
13#[allow(clippy::too_many_arguments)]
18pub fn forward_linear_attention<Q: QuantWeights>(
19 gpu: &dyn GpuBackend,
20 cfg: &Qwen35ForwardConfig,
21 k: &Qwen35Kernels,
22 layer: &LinearAttentionLayer<'_, Q>,
23 state: &LinearAttentionState,
24 scratch: &LinearAttentionScratch,
25 x_in: DevicePtr,
26 x_buf: DevicePtr,
27 stream: u64,
28 intra_dump: Option<&dyn Fn(&str, DevicePtr, u32) -> Result<()>>,
29) -> Result<DevicePtr> {
30 gpu.launch_typed(
32 k.rms,
33 [1, 1, 1],
34 [128, 1, 1],
35 0,
36 stream,
37 &[
38 KernelArg::Bytes(&cfg.hidden.to_le_bytes()),
39 KernelArg::Bytes(&cfg.rms_eps.to_le_bytes()),
40 KernelArg::Buffer(x_in),
41 KernelArg::Buffer(layer.input_ln),
42 KernelArg::Buffer(scratch.x_norm),
43 ],
44 )?;
45 layer.in_proj_a.gemv_gate_up_with(
47 layer.in_proj_b,
48 gpu,
49 scratch.x_norm,
50 scratch.dt_raw,
51 scratch.b_raw,
52 stream,
53 )?;
54 layer
55 .in_proj_qkv
56 .gemv(gpu, scratch.x_norm, scratch.qkv, stream)?;
57 layer
58 .in_proj_z
59 .gemv(gpu, scratch.x_norm, scratch.z, stream)?;
60
61 let batch_one: u32 = 1;
64 let block_x: u32 = cfg.k_head_dim_lin;
65 let qkv_total_lin = cfg.qkv_total_lin();
66 let blocks_per_batch = qkv_total_lin.div_ceil(block_x);
67 let qk_channels: u32 = 2 * cfg.num_k_heads_lin * cfg.k_head_dim_lin;
68 let l2_eps: f32 = 1e-6;
69 gpu.launch_typed(
70 k.conv1d,
71 [blocks_per_batch * batch_one, 1, 1],
72 [block_x, 1, 1],
73 0,
74 stream,
75 &[
76 KernelArg::Buffer(state.conv1d_state),
77 KernelArg::Buffer(scratch.qkv),
78 KernelArg::Buffer(layer.conv1d_weight),
79 KernelArg::Buffer(scratch.qkv_smooth),
80 KernelArg::Bytes(&batch_one.to_le_bytes()),
81 KernelArg::Bytes(&qkv_total_lin.to_le_bytes()),
82 KernelArg::Bytes(&cfg.conv_kernel_size.to_le_bytes()),
83 KernelArg::Bytes(&qk_channels.to_le_bytes()),
84 KernelArg::Bytes(&cfg.k_head_dim_lin.to_le_bytes()),
85 KernelArg::Bytes(&l2_eps.to_le_bytes()),
86 ],
87 )?;
88 let num_state_heads = cfg.num_state_heads();
95 gpu.launch_typed(
96 k.gdn_gate,
97 [num_state_heads.div_ceil(32), 1, 1],
98 [32, 1, 1],
99 0,
100 stream,
101 &[
102 KernelArg::Bytes(&num_state_heads.to_le_bytes()),
103 KernelArg::Buffer(scratch.dt_raw),
104 KernelArg::Buffer(layer.dt_bias),
105 KernelArg::Buffer(layer.a_log),
106 KernelArg::Buffer(scratch.gate),
107 ],
108 )?;
109 gpu.launch_typed(
111 k.sigmoid,
112 [num_state_heads.div_ceil(32), 1, 1],
113 [32, 1, 1],
114 0,
115 stream,
116 &[
117 KernelArg::Bytes(&num_state_heads.to_le_bytes()),
118 KernelArg::Buffer(scratch.b_raw),
119 KernelArg::Buffer(scratch.beta),
120 ],
121 )?;
122
123 let k_offset = (cfg.num_k_heads_lin * cfg.k_head_dim_lin) as usize * 2;
125 let v_offset = (2 * cfg.num_k_heads_lin * cfg.k_head_dim_lin) as usize * 2;
126 let q_view = scratch.qkv_smooth;
127 let k_view = scratch.qkv_smooth.offset(k_offset);
128 let v_view = scratch.qkv_smooth.offset(v_offset);
129
130 let batch_size = 1u32;
132 let total_groups = cfg.num_v_heads_lin * batch_size;
133 gpu.launch_typed(
134 k.gdn_dec,
135 [total_groups, 1, 1],
136 [128, 1, 1],
137 0,
138 stream,
139 &[
140 KernelArg::Buffer(state.gdn_state),
141 KernelArg::Buffer(q_view),
142 KernelArg::Buffer(k_view),
143 KernelArg::Buffer(v_view),
144 KernelArg::Buffer(scratch.gate),
145 KernelArg::Buffer(scratch.beta),
146 KernelArg::Buffer(scratch.y),
147 KernelArg::Bytes(&batch_size.to_le_bytes()),
148 KernelArg::Bytes(&cfg.num_k_heads_lin.to_le_bytes()),
149 KernelArg::Bytes(&cfg.num_v_heads_lin.to_le_bytes()),
150 KernelArg::Bytes(&cfg.k_head_dim_lin.to_le_bytes()),
151 KernelArg::Bytes(&cfg.v_head_dim_lin.to_le_bytes()),
152 ],
153 )?;
154
155 gpu.launch_typed(
157 k.rms,
158 [cfg.num_v_heads_lin, 1, 1],
159 [128, 1, 1],
160 0,
161 stream,
162 &[
163 KernelArg::Bytes(&cfg.v_head_dim_lin.to_le_bytes()),
164 KernelArg::Bytes(&cfg.rms_eps.to_le_bytes()),
165 KernelArg::Buffer(scratch.y),
166 KernelArg::Buffer(layer.norm_weight),
167 KernelArg::Buffer(scratch.y_norm),
168 ],
169 )?;
170
171 layer
173 .out_proj
174 .gemv_silu_gate(gpu, scratch.z, scratch.y_norm, scratch.out, stream)?;
175
176 gpu.launch_typed(
178 k.add_rms,
179 [1, 1, 1],
180 [128, 1, 1],
181 0,
182 stream,
183 &[
184 KernelArg::Bytes(&cfg.hidden.to_le_bytes()),
185 KernelArg::Bytes(&cfg.rms_eps.to_le_bytes()),
186 KernelArg::Buffer(x_in),
187 KernelArg::Buffer(scratch.out),
188 KernelArg::Buffer(layer.post_ln),
189 KernelArg::Buffer(scratch.x_resid),
190 KernelArg::Buffer(scratch.x_norm2),
191 ],
192 )?;
193 layer.gate_proj.gemv_gate_up_with(
195 layer.up_proj,
196 gpu,
197 scratch.x_norm2,
198 scratch.gate_act,
199 scratch.up_act,
200 stream,
201 )?;
202 layer.down_proj.gemv_silu_gate_resid(
204 gpu,
205 scratch.gate_act,
206 scratch.up_act,
207 scratch.x_resid,
208 scratch.x_final,
209 stream,
210 )?;
211
212 if let Some(dump) = intra_dump {
214 gpu.synchronize(stream)?;
215 let z_dim_lin = cfg.z_dim_lin();
216 dump("gdn_x_norm", scratch.x_norm, cfg.hidden)?;
217 dump("gdn_qkv_pre", scratch.qkv, qkv_total_lin)?;
218 dump("gdn_qkv_smooth", scratch.qkv_smooth, qkv_total_lin)?;
219 dump("gdn_y", scratch.y, z_dim_lin)?;
220 dump("gdn_y_norm", scratch.y_norm, z_dim_lin)?;
221 dump("gdn_out", scratch.out, cfg.hidden)?;
222 dump("gdn_x_resid", scratch.x_resid, cfg.hidden)?;
223 dump("gdn_x_final", scratch.x_final, cfg.hidden)?;
224 }
225
226 gpu.copy_d2d_async(scratch.x_final, x_buf, cfg.hidden as usize * 2, stream)?;
229 Ok(x_buf)
230}