ssm_conv: channels-major input mode to drop the delta-net transpose#52
Merged
Conversation
roberteg16
force-pushed
the
rogarcia.qwen3.6-moe-optimization
branch
3 times, most recently
from
July 15, 2026 12:16
5432325 to
d0c4efc
Compare
roberteg16
requested review from
Annieren,
Copilot,
jimw567,
liangliangchang and
mgehre-amd
July 15, 2026 15:23
roberteg16
marked this pull request as ready for review
July 15, 2026 15:24
There was a problem hiding this comment.
Pull request overview
This PR introduces an opt-in channels-major input layout for ggml_ssm_conv and updates the Qwen3.5/3.6 hybrid (DeltaNet + MoE) graphs to use it, eliminating a costly transpose in prefill and improving large-ubatch performance.
Changes:
- Add
GGML_SSM_CONV_LAYOUT_{TIME_MAJOR,CHANNELS_MAJOR}, a new entry pointggml_ssm_conv_channels_major(), and plumb layout through op params so CPU/CUDA can select the correct stride interpretation. - Update DeltaNet conv-state construction in the Qwen graphs to keep
[channels, time, seq]and callggml_ssm_conv_channels_major()directly (dropping the DeltaNet-side transpose). - Extend backend-op tests to exercise the new layout; gate non-CPU/CUDA backends to time-major until they implement channels-major.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/test-backend-ops.cpp | Adds coverage for the channels-major SSM_CONV mode in backend op tests. |
| src/models/qwen3next.cpp | Switches Qwen3Next to call ggml_ssm_conv_channels_major() for conv. |
| src/models/qwen35moe.cpp | Switches Qwen3.6 MoE graph to the channels-major SSM_CONV entry point. |
| src/models/qwen35.cpp | Switches Qwen3.5 graph to the channels-major SSM_CONV entry point. |
| src/models/delta-net-base.cpp | Reworks conv-state concat to be channels-major and removes the DeltaNet transpose. |
| ggml/src/ggml.c | Adds layout-aware SSM_CONV constructor + new channels-major API + layout getter. |
| ggml/src/ggml-webgpu/ggml-webgpu.cpp | Disables channels-major offload on WebGPU (time-major only). |
| ggml/src/ggml-vulkan/ggml-vulkan.cpp | Disables channels-major offload on Vulkan (time-major only). |
| ggml/src/ggml-sycl/ggml-sycl.cpp | Disables channels-major offload on SYCL (time-major only). |
| ggml/src/ggml-opencl/ggml-opencl.cpp | Disables channels-major offload on OpenCL (time-major only). |
| ggml/src/ggml-metal/ggml-metal-device.m | Disables channels-major offload on Metal (time-major only). |
| ggml/src/ggml-hexagon/ggml-hexagon.cpp | Disables channels-major offload on Hexagon (time-major only). |
| ggml/src/ggml-cuda/ssm-conv.cu | Implements channels-major-aware CUDA kernels for SSM_CONV (short/long token paths). |
| ggml/src/ggml-cuda/ggml-cuda.cu | Updates CUDA supports-op logic to interpret d_inner based on layout. |
| ggml/src/ggml-cpu/ops.cpp | Makes the CPU SSM_CONV implementation layout-aware via computed channel/time strides. |
| ggml/src/ggml-cann/ggml-cann.cpp | Disables channels-major offload on CANN (time-major only). |
| ggml/include/ggml.h | Exposes the new layout enum, channels-major entry point, and layout getter in the public API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
roberteg16
force-pushed
the
rogarcia.qwen3.6-moe-optimization
branch
from
July 15, 2026 16:25
d0c4efc to
3a7b9ad
Compare
Materialize the transposed qkv_mixed before ggml_concat so the conv-state prepend takes the coalesced concat_cont path instead of concat_non_cont, which scaled super-linearly with chunk size (CONCAT ~1166ms -> ~34ms at ub=4096, -899ms total prefill).
Delta-net fed ggml_ssm_conv a transposed (time-major) conv input, forcing a physical transpose. Option A removed the quadratic concat but left a 221ms CONT (transpose) kernel at ub=4096. Add an opt-in channels-major layout to ggml_ssm_conv (op_params[0]=1): input is [d_inner, d_conv-1+n_t, n_s] (channels contiguous); output layout unchanged. Implemented on CPU and CUDA (both short and long-token kernels, coalesced smem load); other backends' supports_op reject mode!=0 so the scheduler falls back to CPU. build_conv_state now keeps qkv_mixed channels- major, prepends the recurrent conv state along the time axis (concat dim 1), and calls ggml_ssm_conv_channels_major (qwen35/qwen35moe/qwen3next). Prefill pp4096 ub=4096: CONT 221->10ms, total 2925->2717ms (-1108ms vs the pre-optimization baseline). Validated: test-backend-ops SSM_CONV channels- major cases pass (CUDA vs CPU); Qwen3.6 perplexity 3.40 (healthy).
roberteg16
force-pushed
the
rogarcia.qwen3.6-moe-optimization
branch
from
July 15, 2026 18:36
3a7b9ad to
20191d0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ggml_ssm_convrequires a time-major input[time, channels, seqs], while the delta-net projection produces channels-major[channels, time, seqs]. Feeding the op therefore required a physical transpose of the conv input on every layer, which is a significant kernel in prefill.This PR adds an opt-in channels-major layout to
ggml_ssm_conv(ggml_ssm_conv_channels_major): the input is[d_inner, d_conv-1+n_t, n_s](channels contiguous) and the output layout is unchanged.build_conv_statekeepsqkv_mixedchannels-major, prepends the recurrent conv state along the time axis (concat dim 1), and calls the new entry point, so the transpose is no longer needed.qwen35,qwen35moe, andqwen3nextgraphs.The two commits are: (1) force the conv-state concat input contiguous; (2) add the channels-major
ssm_convmode and switchbuild_conv_stateto it.Measured impact — roofline (Qwen3.6-35B-A3B, prefill pp4096, gfx1151)
-ub 4096 -b 4096(M=4096)−1108 ms (−29 %).
-ub 4096 -b 2048(M=2048;n_ubatchis clamped ton_batch, which defaults to 2048)−196 ms (−7.0 %).
Measured impact — throughput A/B (
llama-bench, Qwen3.6-35B-A3B, gfx1151, t/s, CUDA graphs enabled)Baseline is the branch point
1847907d6.Validation
Kernel numerics.
test-backend-ops -o SSM_CONVruns the channels-major cases (short- and long-token paths,d_conv ∈ {3,4,9},d_inner ∈ {1024,1536,2048}, single/multi-seq) against the CPU reference: 90/90 pass.Output-preserving. Perplexity over a fixed ~500-token passage (
-c 256, greedy) is bit-identical between baseline (1847907d6) and this PR on all three affected architectures:qwen35qwen35moeqwen3nextQwen3.5-35B-A3B (
qwen35moe) additionally runs at PPL 2.18.Notes / out of scope
ggml-backend-meta.cpptensor-parallel split handles the channels-major layout (d_inner is sx axis 0 / c axis 1). Not exercised in a tensor-parallel run here.