Forward with streaming cache: uses cached context instead of zero-padding.
(&self, x: &Tensor, cache: &mut StreamingConvCache)
| 157 | |
| 158 | /// Forward with streaming cache: uses cached context instead of zero-padding. |
| 159 | fn forward_cached(&self, x: &Tensor, cache: &mut StreamingConvCache) -> Result<Tensor> { |
| 160 | let channels = x.dim(1)?; |
| 161 | |
| 162 | let h = self.backend.rms_norm_channel(x, &self.norm_weight, self.eps)?; |
| 163 | let (slot, is_first) = cache.take_slot(); |
| 164 | let context = if is_first { |
| 165 | Tensor::zeros((h.dim(0)?, channels, 6), h.dtype(), h.device())? |
| 166 | } else { |
| 167 | cache.get(slot).unwrap().clone() |
| 168 | }; |
| 169 | |
| 170 | // Update cache: last 6 samples of [context, h] |
| 171 | let h_len = h.dim(2)?; |
| 172 | if h_len >= 6 { |
| 173 | cache.set(slot, h.narrow(2, h_len - 6, 6)?); |
| 174 | } else { |
| 175 | // h shorter than 6: take from context + h |
| 176 | let ctx_take = 6 - h_len; |
| 177 | let ctx_part = context.narrow(2, 6 - ctx_take, ctx_take)?; |
| 178 | cache.set(slot, Tensor::cat(&[&ctx_part, &h], 2)?); |
| 179 | } |
| 180 | |
| 181 | // Fused conv reads from [context, h] virtually — no cat allocation |
| 182 | let h = self.backend.depthwise_conv1d_bias_ctx( |
| 183 | &context, &h, &self.mixer_weight, &self.mixer_bias, 7, channels, |
| 184 | )?; |
| 185 | let x = self.backend.add_scaled(x, &h, &self.gamma)?; |
| 186 | |
| 187 | let h = self.backend.rms_norm_channel(&x, &self.ffn_norm_weight, self.eps)?; |
| 188 | let h = h.transpose(1, 2)?; |
| 189 | let h = self.backend.linear_forward(&h, &self.ffn_linear1_weight, self.ffn_linear1_bias.as_ref())?; |
| 190 | let h = self.backend.gelu(&h)?; |
| 191 | let h = self.backend.linear_forward(&h, &self.ffn_linear2_weight, self.ffn_linear2_bias.as_ref())?; |
| 192 | let h = h.transpose(1, 2)?; |
| 193 | self.backend.add_scaled(&x, &h, &self.ffn_gamma) |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | /// One decoder stage: upsample + N blocks. |
no test coverage detected