Skip to content

ggml : backport persistent view initialization after buffer-size splits - #37

Open
arc-uri-el wants to merge 2 commits into
halo-box:masterfrom
arc-uri-el:contribution/view-allocation-20260908
Open

arc-uri-el wants to merge 2 commits into
halo-box:masterfrom
arc-uri-el:contribution/view-allocation-20260908

Conversation

@arc-uri-el

@arc-uri-el arc-uri-el commented Sep 8, 2026 •

Copy link
Copy Markdown

Overview

Backport Yoshinao Kadowaki's allocator fix and regression from ggml-org#25584. The original authors and cherry-pick provenance are retained:

  • Root fix: 335c0989ed71e0207b212da063a40a5845a00582 -> 14aa71aa144e5c51660ecf3cf75de375cd568e33
  • Regression: 0074731c9a0ee2b453fe222d59a4aa24ad9cb696 -> 72dbbbd207c3f5ceaaff3177bac821212f612682

The test insertion conflicted with newer Halo tests; both sets are retained. No state-I/O skip guards, new checkpoint APIs or unrelated scheduling changes are included. This is not a claim to have authored the upstream fix.

On the current tested Halo base, a large KV parent exceeds the Vulkan buffer type's maximum allocation chunk. A trailing view-only range is left without ggml_backend_view_init. Saving a displaced conversation then aborts on that nonempty view. The change allocates parents within ranges and initializes views after all ranges have been allocated.

Fresh diagnosis on the Radeon 8060S identified:

view:   cache_k_l63 (view), Q8_0, ne=[1024,1000192,1,1]
read:   offset=0, size=834496, data=NULL, buffer=NULL
parent: cache_k_l63, data=0x1000, buffer non-NULL, view_offset=0

The view represents live KV data. A !tensor->data guard in state I/O would conceal the allocation failure and omit that data; it is not the repair submitted here. The diagnostic interposer only printed these fields and then called the original reader, preserving the abort.

Measurements

Device:     Ryzen AI MAX+ 395 / ASUS ROG Flow Z13 GZ302EAC
Memory:     128 GB LPDDR5X
Power:      AC; performance platform profile and CPU governor; GPU clocks auto
Firmware:   BIOS 304; PPT settings 80/92/93 W; VRAM 512 MiB
Kernel:     Linux 7.3.0-rc1; ttm.pages_limit=30408704; GTT 116 GiB
Backend:    Radeon 8060S Vulkan RADV, Mesa 26.2.0
Build:      GCC 15.2, Release, shared libraries, native CPU, Vulkan, OpenMP; -j8
Baseline:   7449a0fe9710ab584c5f9a6d25e7a31eea2708b8
Change:     72dbbbd207c3f5ceaaff3177bac821212f612682
Models:     official Qwen3.8-27B Q4_K_M + Q8_0 MTP; separate custom Qwen3.8-27B quant witness

The baseline and backport were built independently in this session with identical compiler/backend settings and no compiler warnings. Mapped libraries were verified for each test. The subsequent master merge 99a40a3e6 changes speculative replay, not this allocation path; it is not included in these measured builds.

Baseline / after:

Test Baseline Backport
Same test-alloc executable with view-tail regression abort at uninitialized view all 17 cases pass
Official Qwen: shallow request then displaced conversation SIGABRT during RAM state save completes normally
Official Qwen: return to displaced 512-token prompt unreachable after abort 508 cached + 4 fresh; identical 256 generated IDs
Custom Qwen quant: same displacement and return SIGABRT same successful recovery and exact IDs

The official witness uses the unmodified files in ggml-org/Qwen3.8-27B-GGUF:

  • Qwen3.8-27B-Q4_K_M.gguf: SHA-256 31629f53165ab6a7dad8c9847dcfd1fdf55829dac1e6e748f4a68581b0033d34
  • mtp-Qwen3.8-27B-Q8_0.gguf: SHA-256 cbf60a0c48b431bb61f1d49b8948dc88ac29c398d6dbdbbb2e6e89ef77eacc9a

The server tuple was one unified slot with 1,000,000 configured tokens (1,000,192 physical padding), Q8_0 target KV, Q4_0 draft KV, MTP depth 3, batch 2048/512, 32 checkpoints and 16 GiB RAM cache. The requests were a 512-token shallow prompt with 256 generated tokens, a different 5,000-token prefix, then the original shallow prompt. The 256-token cap is an output-identity control, not a completed-task claim.

Relevant server arguments for the allocation and prompt-cache boundary:

llama-server -m Qwen3.8-27B-Q4_K_M.gguf -md mtp-Qwen3.8-27B-Q8_0.gguf \
  -dev Vulkan0 -ngl all -sm none -fa on --load-mode none -fit off \
  --override-kv qwen35.context_length=int:1000000 -c 1000000 \
  --rope-scaling yarn --rope-scale 4 --yarn-orig-ctx 262144 \
  -np 1 --kv-unified --kv-unified-per-slot 1000000 -cb \
  -ctxcp 32 -cms 8192 -b 2048 -ub 512 -ctk q8_0 -ctv q8_0 \
  --cache-ram 16384 --cache-prompt --slot-prompt-similarity 0.10 \
  --spec-type draft-mtp --spec-draft-n-max 3 --spec-draft-n-min 0 \
  --spec-draft-p-min 0 --spec-draft-ngl all --spec-draft-device Vulkan0 \
  -ctkd q4_0 -ctvd q4_0 --host 127.0.0.1

Correctness:

test-alloc includes the upstream small dummy-backend reproducer, so reaching the view-tail bug does not require a large model. All 17 tests pass on the backport; the same executable aborts at the new view check when linked to the baseline libraries.

A separate official-Qwen Vulkan control used each of the repository's prose, code, structured and numeric corpora, repeated/truncated to 512 tokens, followed by eight greedy steps. All nine complete logit rows, prompt IDs and selected IDs were byte-identical before and after for each corpus, with Q8_0 KV and identical settings. The model processes exited normally and were confirmed gone.

This backport makes no throughput claim. llama-bench, perplexity and the full backend-op suite were not run. The evidence establishes the allocator view-init regression, actual affected Vulkan state save/recovery and bounded output preservation, not global backend qualification.

Requirements

  • I have read and agree with the contributing guidelines.
  • This upstream backport is justified here by a fresh, reproducible Strix Halo Vulkan failure and matched fixed run. The original upstream PR remains the source of the fix.
  • AI usage disclosure: AGENT-ASSISTED BACKPORT. GPT 6 Astra reproduced and diagnosed this Halo failure, compared the upstream patch, resolved only the test insertion conflict, ran the tests and prepared this PR at the submitting account owner's explicit direction. The original PR discloses Cursor assistance. Original authorship and upstream commit references are preserved; no human review/test attestation is invented. The submitting account owns this backport's follow-up.
  • What was NOT verified: other GPUs, other operating systems, full occupied 1M contexts, every allocation-failure path, the full backend-op/performance matrix or perplexity. Earlier empty-state interpretations were refuted by the live parent-storage diagnosis and are not used as evidence here.

When ggml_backend_alloc_ctx_tensors_from_buft splits allocation on
buft max_size, a view-only tail at the end of the context could skip
the final alloc_tensor_range. Persistent views (e.g. KV k_stream /
v_stream) were then left without ggml_backend_view_init.

Allocate only parent tensors in alloc_tensor_range and initialize all
views in a final pass over the context after all splits complete.

(cherry picked from commit 335c098)
Cover the buft max_size split path where a view-only tail would skip
ggml_backend_view_init without the finalize pass.

(cherry picked from commit 0074731)
@dzannotti

Copy link
Copy Markdown
Collaborator

Wouldn’t this be picked up by an upstream rebase?

@dzannotti dzannotti left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation: Release test-alloc passed, including test_view_init_after_max_size_split.

Scope: this is a general allocator correctness backport, not a gfx1151 change, and the branch is behind master. Please land/stage it in halo-box/llama.cpp/upstream (or rebase if master already contains it), then sync the Strix fork instead of carrying a permanent local divergence.

comment generated by my clanker Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants