| 231 | // (mirrors the Metal supports gate); otherwise keep the mul_mat + add chain |
| 232 | const bool use_acc = in_channels >= 64 && output_frames > 8; |
| 233 | ggml_tensor * acc = nullptr; |
| 234 | for (int64_t tap = 0; tap < kernel_size; ++tap) { |
| 235 | // columns[c, j] = input[tap * dilation + j, c]: contiguous column view of input_cf. |
| 236 | auto * columns = ggml_view_2d( |
| 237 | ctx.ggml, |
| 238 | input_cf, |
| 239 | in_channels, |
| 240 | output_frames, |
| 241 | input_cf->nb[1], |
| 242 | static_cast<size_t>(tap * dilation) * in_channels * sizeof(float)); |
| 243 | auto * tap_weights = ggml_view_2d( |
| 244 | ctx.ggml, |
| 245 | weight_taps, |
| 246 | in_channels, |
| 247 | out_channels, |
| 248 | weight_taps->nb[1], |
| 249 | static_cast<size_t>(tap) * in_channels * sizeof(float)); |
| 250 | if (acc == nullptr) { |
| 251 | acc = ggml_mul_mat(ctx.ggml, tap_weights, columns); |
| 252 | } else if (use_acc) { |
| 253 | acc = ggml_mul_mat_acc(ctx.ggml, tap_weights, columns, acc); |
| 254 | } else { |
| 255 | acc = ggml_add(ctx.ggml, acc, ggml_mul_mat(ctx.ggml, tap_weights, columns)); |
| 256 | } |
| 257 | } |
| 258 | return acc; |
| 259 | } |
| 260 | |
| 261 | // Metal fast path for stride-1 conv1d on the time-fast [frames, channels] layout used by |
| 262 | // the audio codecs. ggml_conv_1d materializes an im2col matrix whose kernel taps are |
| 263 | // strided gathers in this layout (~200 ms per conv at [569k, 96] on M4); instead, transpose |
| 264 | // the input to channel-fast once, run one contiguous GEMM per kernel tap, and transpose |
| 265 | // the accumulator back (~3-6x faster). |
| 266 | core::TensorValue build_conv1d_pertap_fast_path( |
| 267 | core::ModuleBuildContext & ctx, |
| 268 | const Conv1dConfig & config, |
| 269 | const core::TensorValue & input, |
| 270 | const core::TensorValue & weight_f32, |
| 271 | const core::TensorShape & output_shape) { |
| 272 | // channel-fast copy of the input: [IC, frames]; kernel taps become contiguous columns. |
| 273 | auto * input_cf = ggml_cont(ctx.ggml, ggml_transpose(ctx.ggml, input.tensor)); |
| 274 | auto * acc = conv1d_pertap_gemm_channel_fast( |
| 275 | ctx, |
| 276 | input_cf, |
| 277 | weight_f32.tensor, |
| 278 | config.in_channels, |
| 279 | config.out_channels, |
| 280 | config.kernel_size, |
| 281 | config.dilation, |
| 282 | output_shape.dims[2]); |
| 283 | // mul_mat yields [OC, frames]; restore the canonical [frames, OC] orientation. |
| 284 | return core::wrap_tensor( |
| 285 | ggml_cont(ctx.ggml, ggml_transpose(ctx.ggml, acc)), |
| 286 | output_shape, |
| 287 | GGML_TYPE_F32); |
| 288 | } |
| 289 | |
| 290 | int64_t conv2d_output_dim(int64_t input, int kernel, int stride, int padding, int dilation) { |
no test coverage detected