Repository navigation
delta_net_base: refactor conv state - #30878
Conversation
|
Probably this will not work with |
The |
|
That's weird, it fails for me with |
|
Which model did you try? I tried: and this works. |
|
You need to try with MTP |
|
Okay now it works with |
| case GGML_OP_ARANGE: { | ||
| // no srcs, every device computes the same values | ||
| split_state = {GGML_BACKEND_SPLIT_AXIS_MIRRORED, {0}, {1}, 1}; | ||
| } break; | ||
| case GGML_OP_PAD_REFLECT_1D: | ||
| case GGML_OP_ROLL: | ||
| case GGML_OP_ARANGE: |
There was a problem hiding this comment.
Please keep the ops in the same order they're declared in ggml.h.
There was a problem hiding this comment.
The ARANGE case is gone, the slot index is now a host input, so the op order matches ggml.h again.
| // permuted view: split the view dim that spans the src split axis, other dims must be inside one element of it or outside the span | ||
| if (ggml_is_permuted(tensor) && !ggml_is_permuted(tensor->src[0]) && axis >= 0 && axis < GGML_MAX_DIMS-1) { | ||
| const size_t nb_axis = tensor->src[0]->nb[axis]; | ||
| const size_t nb_next = tensor->src[0]->nb[axis+1]; | ||
| for (int dim = 0; dim < GGML_MAX_DIMS; dim++) { | ||
| if (tensor->ne[dim] == 1 || tensor->nb[dim] < nb_axis || tensor->nb[dim]*tensor->ne[dim] != nb_next) { | ||
| continue; | ||
| } | ||
| bool others_ok = true; | ||
| for (int i = 0; i < GGML_MAX_DIMS; i++) { | ||
| if (i == dim || tensor->ne[i] == 1) { | ||
| continue; | ||
| } | ||
| others_ok = others_ok && (tensor->nb[i]*tensor->ne[i] <= tensor->nb[dim] || tensor->nb[i] % nb_next == 0); | ||
| } | ||
| if (others_ok) { | ||
| return {ggml_backend_meta_split_axis(dim), {0}, {1}, 1}; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
What tensor shapes does this cover and where in the modified compute graphs does this show up?
There was a problem hiding this comment.
Note that I don't fully understand the meta backend change
This just fixes the permuted view of tensor is not supported in the meta backend. This is triggered by using the refactor in this PR, MTP + -sm tensor
There was a problem hiding this comment.
The only permuted view in this PR is the slots view of conv_states_all in build_conv_state, shaped [d_conv - 1, n_rs_seq + 1, channels, n_seqs] with the slot stride larger than the channel stride, so it is only built with n_rs_seq > 0 (MTP or another draft that needs rollback). The source is split along channels and the new code maps that split to dim 2 of the view.
3982664 to
d9958cc
Compare
I tried combining both this PR and #30499 on top of latest |
|
Yes we need to port this change to glm5.3 next. Will push something soon |
|
Tested in a rootless container with an RTX PRO 6000, llama-server with Qwen3.8-27B UD-Q8_K_XL and MTP only (--spec-draft-n-max 3, so 4 rollback slots), greedy, a 24.5k token prompt then 512 generated tokens, against the base of this PR. Without this PR decode runs at 97.66 t/s. With this PR decode runs at 97.93 t/s, so +0.27%, with the same output token for token and the same 345/495 accepted drafts. Prefill is unchanged within noise. This PR does not touch qwen4exp, which has its own build_conv_state_at, so #31443 is independent of it. @am17an small nit, nothing blocking: the slot index could be a host filled I32 input instead of arange + argsort, which would also drop the ARANGE case from the meta backend. Happy to push that commit on your branch if you want. |
The rollback slot index of the conv windows is a constant per graph shape. It is now an I32 input of the recurrent memory filled on the host, instead of an arange and an argsort computed on the device, and the meta backend no longer needs a mirrored split for ARANGE.
|
@ServeurpersoCom did you check prefill at 2048 sized batches? because that's where it would matter |
Yes, the prefill numbers are with -b 4096 -ub 2048, so the 24.5k prompt goes through as 12 ubatches of 2048 with MTP active. Prefill stays within noise: 4144 t/s without this PR and 4160 t/s with it, with about 1% spread between runs. Decode is where it shows: 97.66 t/s without this PR and 97.93 t/s with it, and the runs do not overlap (97.64 to 97.68 vs 97.91 to 97.95). On a dense 27B that is bound by memory bandwidth, there is very little left to win on the graph side, so a stable gain there is worth having. When decode is this memory bound, the per node cost is one of the few things left to shave, so fewer nodes is a free win. |
There was a problem hiding this comment.
LGTM, decode gain is small but reproducible with identical output, and the branch merges cleanly with master. The gpu-webgpu-apple failure is the deepseek4 comp_padding reallocation fixed on master by #31422, a re-run rebase will pick it up.
Also checked --parallel 4 with MTP (4 concurrent requests, greedy, 6 runs on each side): batching makes the output non deterministic on master too, and this PR forks at the same points with the same acceptance, while sequential runs are identical. Pure CPU gives identical output in both sequential and parallel runs, at the same speed.
Picked from ggml-org#30878 (open at the time of picking), head d9958cc, net diff against its merge base 2280f0f: 2e7a5fb delta_net: access conv state via views d9958cc fix sm tensor
|
For the record, rocm and vulkan-amd are not from this PR: they stop about 10 seconds after a successful checkout with "Can't find action.yml under .github/actions/ccache-buckets" on the new ggml-hfc-amd-r9700 runners, which run each step through Kubernetes container hooks, and the same failure shows up on other PRs. gpu-rocm stays queued until the 24h cancel, since no runner carries the self-hosted Linux AMD labels anymore. WebGPU is green locally with #31422 on top: the main ctest suite passes and test-llama-context passes the rollback cases on qwen35 and qwen35moe. |
|
@ServeurpersoCom will you create a follow-up for doing this for qwen4exp and glm5.3-next or should I? |
| // window w starts at column n - (Kw - 1) + w (ascending time) and is rollback slot Kw - 1 - w | ||
| // slots reaching back past this ubatch are left unwritten, like the ssm snapshots in build_recurrent_attn | ||
| // the windows overlap, so the view is created with one window and widened after the size check | ||
| ggml_tensor * windows = ggml_view_4d(ctx0, conv_input, | ||
| conv_kernel_size - 1, 1, conv_channels, n_seqs, | ||
| ggml_element_size(conv_input), conv_input->nb[1], conv_input->nb[2], | ||
| ggml_row_size(conv_input->type, n - (Kw - 1))); | ||
| windows->ne[1] = Kw; |
There was a problem hiding this comment.
I'm taking a second look at the change and I don't get why we need to widen the view like this. What is the issue with directly using Kw in the ggml_view_4d?
There was a problem hiding this comment.
because of this reason, which I guess should be fixed in ggml, i.e. it doesn't take into account the nb, and the view has to be overlapping
Lines 1802 to 1809 in ac8e169
Yes, I'll take it, as two follow-ups. The first one is in ggml: the bound check of a view will be computed from its strides, like ggml_nbytes does, instead of assuming a contiguous layout. Overlapping views then become legal as they are, and the widening trick Georgi asked about goes away. The second one ports the single set_rows write to the other archs that still copy the conv state once per rollback slot, starting with qwen4exp and glm5-next. I'll go with ggml first so the second one can use clean views from the start. |
…ore of the rollback slots (ggml-org#30878) Since ggml-org#30878 qwen35 / qwen35moe write the K rollback slots of the conv state with one set_rows over min(n_t + 1, K) overlapping windows of the conv input (index rs_s_slot_rev = {Kw - 1, ..., 0}) instead of K copies, so the fusion stopped matching on every model with rollback slots (MTP / n-gram drafts). Match that form as Kw slots: slot s gets the columns starting at n_t - s, slots >= Kw stay unwritten, as in the unfused graph. test-backend-ops: GDN_DECODE_FUSION sr variants. Assisted-by: Claude Opus 5.5 (Claude Code)
…ore of the rollback slots (ggml-org#30878) Since ggml-org#30878 qwen35 / qwen35moe write the K rollback slots of the conv state with one set_rows over min(n_t + 1, K) overlapping windows of the conv input (index rs_s_slot_rev = {Kw - 1, ..., 0}) instead of K copies, so the fusion stopped matching on every model with rollback slots (MTP / n-gram drafts). Match that form as Kw slots: slot s gets the columns starting at n_t - s, slots >= Kw stay unwritten, as in the unfused graph. test-backend-ops: GDN_DECODE_FUSION sr variants. Assisted-by: Claude Opus 5.5 (Claude Code)
…ore of the rollback slots (ggml-org#30878) Since ggml-org#30878 qwen35 / qwen35moe write the K rollback slots of the conv state with one set_rows over min(n_t + 1, K) overlapping windows of the conv input (index rs_s_slot_rev = {Kw - 1, ..., 0}) instead of K copies, so the fusion stopped matching on every model with rollback slots (MTP / n-gram drafts). Match that form as Kw slots: slot s gets the columns starting at n_t - s, slots >= Kw stay unwritten, as in the unfused graph. test-backend-ops: GDN_DECODE_FUSION sr variants. Assisted-by: Claude Opus 5.5 (Claude Code)
Overview
Provides a 1-2% speed-up due to less copying of the conv state.
Additional information
Requirements