Repository navigation
Conversation
Build llama.cpp from this branch's source against the latest TheRock nightly ROCm tarball for gfx1151/gfx1150 (Ubuntu), then smoke-test each artifact on the matching self-hosted Strix Halo GPU runner with llama-cli. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The 7.14.0a20260609+ nightlies regressed libhsa-runtime64 (build 1b2a555677), segfaulting in GpuAgent::InitDma on gfx115x runners. See ROCm/TheRock#5763. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The previous gfx1150 runner could not reach huggingface.co (curl 35, connection reset) during model download. Try a different runner. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Self-hosted runners (e.g. the gfx1150 box) can't reach huggingface.co (curl 35, connection reset) but can reach GitHub. Fetch the GGUF from a fixed release asset (jimw567/llamacpp-test-assets) with retries instead. Revert gfx1150 runner to the working label (runs-on matches labels, not machine names, so the prior machine-name value never scheduled). Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…tput
The binary failed to start on runners lacking libatomic1 (exit 127); bundle
libatomic.so.1 with the artifact so $ORIGIN RPATH resolves it.
Replace stale assertions ("offloaded 29/29 layers to GPU", "</think>") with
functional checks against current output: ROCm GPU selected, all layers +
output offloaded to ROCm0, and tokens actually generated (eval timing).
Verified on a Strix Halo gfx1151 box: passes on real GPU output, fails on
CPU-only/no-generation output.
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The previous assertion only checked GPU offload + that >=1 token was emitted. Add functional checks that the model produced a non-empty answer and non-empty reasoning trace, and generated >=20 tokens. Verified against real gfx1151 output: passes on a genuine response, fails on empty/one-token. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Switch the llama-cli smoke test to a single-correct-answer prompt
("What is 2 + 2?") with greedy decoding (--temp 0), and assert the
parsed assistant content is 4. This checks the GPU math path end to
end rather than only that some text was generated.
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
- Publish a dated GitHub Release (b<YYYYMMDD>) with gfx1151 + gfx1150 tar.gz binaries, gated to the nightly workflow_dispatch (create_release input) so push/PR/manual runs never publish. - Wrap the llama-cli smoke test in a 180s timeout that dumps GPU/driver diagnostics on hang, so a stuck runner fails fast with visible logs. - Temporarily disable the gfx1150 on-hardware test (its runner hangs in GPU inference); gfx1150 binaries are still built and released. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Mirror the lemonade llamacpp-rocm setup: build the grouped gfx110X target (gfx1100;gfx1101;gfx1102;gfx1103) from the gfx110X-all ROCm tarball, covering desktop RDNA3 and the Hawk Point/Phoenix iGPU (Radeon 760M/780M). Decouple the build matrix into gfx_target / s3_target / gpu_targets so per-target tarball suffixes and GPU target lists are explicit. Publish the gfx110X binary in the nightly release. No on-hardware test leg (no Hawk Point runner). Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
gfx1153 ships as its own standalone TheRock tarball (no bundle suffix), same as gfx1151/gfx1150. Build+release only — no on-hardware test runner. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
ci: add gfx110X (Hawk Point/Phoenix RDNA3) build + release target
Collapse the 4-leg per-family matrix (gfx1151/gfx1150/gfx1153/gfx110X) into a single build sourced from TheRock's multi-arch tarball. One fat binary covers all current CI arches (gfx1100-1103, gfx1150/1151/1153) and ships as one universal release archive instead of four mostly- duplicate per-family archives. The multi-arch tarball is streamed and pruned at the tar level: drop all .kpack and the Tensile DBs of every non-target arch. The GEMM path llama.cpp uses works from the per-arch Tensile DB alone (validated on gfx1151 hardware: rocBLAS sgemm succeeds with ROCM_KPACK_DISABLE=1), so no .kpack files are bundled. The gfx1151 hardware test job is the end-to-end safety net. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The "universal" vs "multi-arch" wording was redundant — both describe one package covering many arches. Standardize on "multiarch" to match TheRock's upstream vocabulary. Renames the artifact/archive to llama-<TAG>-ubuntu-rocm-multiarch-x64 and updates comments + release body. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Follow-up to the package rename: drop the hyphenated "multi-arch" prose in comments, echoes, and the release body for one consistent spelling. The upstream nightlies endpoint (tarball-multi-arch) and the therock-dist-linux- multiarch- filenames are external names and left untouched. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
ci(gfx11): build one universal multi-arch ROCm package
Add mmq device table for RDNA3.5
# Conflicts: # README.md
Sync fork master with upstream llama.cpp (211 commits)
Build llama.cpp from this branch's source against the latest TheRock nightly ROCm tarball for gfx1151/gfx1150 (Ubuntu), then smoke-test each artifact on the matching self-hosted Strix Halo GPU runner with llama-cli. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The 7.14.0a20260609+ nightlies regressed libhsa-runtime64 (build 1b2a555677), segfaulting in GpuAgent::InitDma on gfx115x runners. See ROCm/TheRock#5763. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The previous gfx1150 runner could not reach huggingface.co (curl 35, connection reset) during model download. Try a different runner. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Self-hosted runners (e.g. the gfx1150 box) can't reach huggingface.co (curl 35, connection reset) but can reach GitHub. Fetch the GGUF from a fixed release asset (jimw567/llamacpp-test-assets) with retries instead. Revert gfx1150 runner to the working label (runs-on matches labels, not machine names, so the prior machine-name value never scheduled). Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…tput
The binary failed to start on runners lacking libatomic1 (exit 127); bundle
libatomic.so.1 with the artifact so $ORIGIN RPATH resolves it.
Replace stale assertions ("offloaded 29/29 layers to GPU", "</think>") with
functional checks against current output: ROCm GPU selected, all layers +
output offloaded to ROCm0, and tokens actually generated (eval timing).
Verified on a Strix Halo gfx1151 box: passes on real GPU output, fails on
CPU-only/no-generation output.
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
The previous assertion only checked GPU offload + that >=1 token was emitted. Add functional checks that the model produced a non-empty answer and non-empty reasoning trace, and generated >=20 tokens. Verified against real gfx1151 output: passes on a genuine response, fails on empty/one-token. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Switch the llama-cli smoke test to a single-correct-answer prompt
("What is 2 + 2?") with greedy decoding (--temp 0), and assert the
parsed assistant content is 4. This checks the GPU math path end to
end rather than only that some text was generated.
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
- Publish a dated GitHub Release (b<YYYYMMDD>) with gfx1151 + gfx1150 tar.gz binaries, gated to the nightly workflow_dispatch (create_release input) so push/PR/manual runs never publish. - Wrap the llama-cli smoke test in a 180s timeout that dumps GPU/driver diagnostics on hang, so a stuck runner fails fast with visible logs. - Temporarily disable the gfx1150 on-hardware test (its runner hangs in GPU inference); gfx1150 binaries are still built and released. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
ci(gfx11): install Node.js so llama-ui builds from source
gfx11 saves the cache only on push (infrequent, plus the daily nightly), so a 1d eviction window prunes still-useful objects between runs. 7d keeps hot objects across the branch's lower push cadence. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
The per-op roofline profiler dlopens librocprofiler-sdk.so.1 at runtime (ggml-cuda-roofline.cpp), but the release only bundled librocprofiler-register.so, so the profiler silently no-op'd and wrote no artifact. Pack librocprofiler-sdk.so* plus its one bundle-missing dep librocm_sysdeps_dw.so.1 (libdw, a hard DT_NEEDED of the sdk lib); the existing $ORIGIN RPATH step lets the dlopen resolve both locally with no LD_LIBRARY_PATH. Add a `roofline` workflow_dispatch input (default true, preserving the prior always-on behavior) gating both -DGGML_HIP_ROOFLINE and the extra lib packing, so lean builds can opt out. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
The comment pointed at utils/gather_required_libs.py to regenerate the ROCm lib copy list, but that script does not exist in the repo; the list is maintained by hand. Remove the dead reference. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
ci(gfx11): bundle librocprofiler-sdk for roofline; add roofline input
ci(gfx11): cache HIP builds with ggml-org/ccache-action
Fused rows previously carried a group HBM byte total computed in-tree. Instead, emit raw per-fused-node facts -- each tensor's byte count, a storage id (view_src root, so aliasing is visible) and a source tensor id -- and let the consumer (rocm-scripts tools/roofline) compute external in/out. This lets the consumer apply the exclusion policy it owns: skip no-op views (VIEW/RESHAPE), drop in-place destinations (e.g. SET_ROWS/CPY writing their own dst), exclude internal tensors, and dedup reads by source. It also matches how non-fused rows already work (producer reports facts, consumer sums). Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Non-fused rows already emit dst/src byte counts; also emit the dst storage id, per-source ids and per-source storage ids (all already captured by fill_head_record). This lets the consumer apply the same in-place-destination rule to single kernels as to fused ones -- e.g. a standalone CPY or SET_ROWS no longer has its write target (dst passed in as a source) double-counted. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Address review: define "tensor id" (a ggml_tensor's address, dedup key) and "storage id" (the view_src-root buffer address, shared by aliasing tensors) at the op_record fields, and drop the vague word "facts" from the surrounding comments in favour of concrete wording. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Address review: the emitted keys and op_record members used cryptic names. Rename dst_sid -> dst_storage_id, src_ids -> src_tensor_ids, src_sids -> src_storage_ids (both the C++ members and the JSON keys). No behavior change. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
… operand swap A mul_mat of a single-column f32 weight (ne01 == 1) against a wide activation (ne11 > MMVF_MAX_BATCH_SIZE) misses the mmvf vector kernel because ne11 is the batch dimension, and falls back to an f32 cuBLAS/rocBLAS GEMM whose 32x32 macro tile computes just one useful output column. Since the output is a vector, the operands can be swapped transpose-free: the activation becomes the matrix and the single-column weight the lone F32 vector, with dst reinterpreted as [ne11, 1]. This runs the op as a proper GEMV. The graph is unchanged, so LoRA and any other consumer are unaffected. On Qwen3.6-35B-A3B (gfx1151, pp128) the shared-expert gate (weight f32[2048,1]) drops from ~78 us/instance on rocBLAS Sgemm to ~3.8 us on mmvf (~20x), ~3.0 ms saved across the 40 layers. Adds test-backend-ops regression cases (m=1, n sweeping across MMVF_MAX_BATCH_SIZE) that validate both the direct and swapped paths against the CPU reference. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Extend the op_record comment: the storage id alone is not enough. The storage id (buffer identity) drives the view/in-place/internal exclusions; the tensor id drives read dedup. They diverge when distinct sources alias one buffer (e.g. two ops reading the two halves of a tensor: one storage id, two tensor ids) -- deduping by storage id would undercount, so dedup keys on the tensor id. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
The full build pulls in the llama-ui asset target, which fetches a prebuilt UI tarball from HF and validates it; that step can fail independently of the HIP backend this check exists to verify. Build the two configs with -DLLAMA_BUILD_UI=OFF -DLLAMA_USE_PREBUILT_UI=OFF. Assisted-by: Claude Opus 4
CUDA: route single-column f32 mul_mat through mmvf (transpose-free operand swap)
fix: ci(hip): drop web UI provisioning from hip-quality-check
roofline: emit raw per-tensor data for DDR byte accounting (single + fused)
MUL_MAT_ID reads only the experts a launch actually routes, but the byte model had no way to know that count and the consumer fell back to the shape bound min(M*top_k, E) -- which saturates to all experts and over-counts HBM traffic when routing leaves experts idle, pushing memory-bound MoE kernels past 100% of peak (the working set dwarfs L2, so it is not cache reuse). Read the routing ids tensor back per invocation and emit the distinct active-expert count as experts_used. begin_op takes the op's stream and hipStreamSynchronize's it so the ids are final before the copy; the read-back is wrapped in a sentinel correlation id (0) so any copy kernel it lowers to is attributed to no op and skipped in the total. Device-timestamp durations are unaffected, so the report's figures are unchanged apart from the new field.
roofline: capture per-launch MoE active-expert count (experts_used)
Two correctness fixes for the GGML_CUDA_GRAPH_OPT fork/join concurrency so that work assigned to a non-default stream actually runs there: - ggml_cuda_op_mul_mat (the legacy tiled dispatcher) hardcoded ctx.stream(id, 0) for the src0 setup and ctx.stream(id, is) for the per-column compute loop. For a single GPU both resolve to stream 0 regardless of curr_stream_no, so any op on this path ran on the main stream while its aux-stream consumers only waited the fork event and read stale data. Use ctx.stream() for the non-split case; the multi-GPU split path keeps its per-device stream 0 for peer synchronization. This is a no-op when concurrency is off (curr_stream_no == 0). - The cuBLAS handle was shared across streams (one per device). The handle carries a workspace that concurrent GEMMs on different streams corrupt, so give each (device, stream) its own handle. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
The attention QKV concurrency (GGML_CUDA_GRAPH_OPT) produced incorrect results because it relied on interleaving the branch nodes in the graph so that ggml-alloc would keep them non-overlapping. ggml-alloc sees the interleaved order while the executor restores the original order, and is_valid() only checks the branches against each other, not against tensors read across the region (e.g. the fork output every branch consumes concurrently). ggml-alloc could therefore recycle such a tensor's memory for a branch scratch, corrupting a concurrent read. Replace the interleave with a dedicated compute buffer for the concurrent branch nodes. Their data/buffer is pre-assigned into one CUDA buffer, reused across layers (which run sequentially) with distinct per-node offsets within a region, so the branches are structurally disjoint from each other and from the rest of the graph. is_valid() then accepts the event without any graph reordering. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Add a shared-expert detector to ggml_backend_cuda_graph_optimize (after the attention pass, under GGML_CUDA_GRAPH_OPT). It matches the diamond join = ggml_add(ffn_moe_out, ffn_shexp*) by callback names and finds the FFN-input norm that forks into both branches. Following vLLM's shared-expert model, the routed experts stay on the main stream and only the shared expert forks onto a single aux stream, joined at the add. Keeping the large routed branch on the main stream (region nodes not in the stream map default to the main stream) avoids migrating it and needs just one fork/join. The shared-expert branch reuses the concurrent-branch scratch buffer so it is disjoint from the routed branch without any graph reordering. Gated to decode (single token). The overlap only helps when the routed branch leaves the GPU underutilized for the shared expert to run alongside it: that holds in decode (batch 1, latency/occupancy-bound) but not in prefill, where the routed matmuls already saturate the GPU and the overlap only adds contention. Output is byte-identical to sequential. Requires GGML_CUDA_GRAPH_OPT, CUDA graphs and a single GPU, so it is off by default. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
ggml-cuda: overlap the MoE shared expert on a separate stream
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).
ssm_conv: channels-major input mode to drop the delta-net transpose
…oE launches count_active_experts already reads the MUL_MAT_ID ids tensor back to host once per invocation to count distinct routed experts; the same pass now also fills a per-expert token-count array. begin_op stores it per invocation and write_report emits it as "tokens_per_expert" (length n_experts, index e = routings to expert e, Σ = n_tokens*top_k) alongside experts_used. No new device work or sync — the ids read-back already happens for experts_used. The consumer renders it as the MoE routing-load distribution under --show-histograms.
roofline: emit per-expert routing histogram (tokens_per_expert) for MoE launches
Author
|
Wrong base branch. |
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.
MMVDQ change summary
What it adds: a dequantize-to-float matvec path (mmvdq) for K-quant weights
during decode (n=1), as an alternative to the existing mul_mat_vec_q (mmvq)
path.
Why: mmvq quantizes the activation vector to q8_1 first (a quantize_q8_1
pass) so it can do int8 dot products. On RDNA3.5 (gfx1151) int8 has no
throughput advantage over f16, and these matvecs are DRAM-bandwidth-bound on
the weights, so the q8_1 quantize pass is pure overhead. mmvdq dequantizes
the weight blocks straight to float and does the dot in float, skipping the
quantize pass entirely.
Scope (files):
launchers
Kernel design (mmvdq.cu): one warp per output block, 16 threads process one
super-block, activations loaded once (as float4/float2) and reused across the
block. Covers Q4_K, Q5_K, Q6_K. Includes a fused gate+up SwiGLU variant
(mul_mat_vec_dq_glu) that computes silu(gate)*up in one dispatch sharing the
activation load.
Routing / gating (ggml-cuda.cu):
Q4_K/Q5_K (or Q6_K if enabled), all tensors contiguous, ne02/03/12/13==1, and
ne[0] % QK_K == 0.
GGML_GLU_OP_SWIGLU, matching gate/up shapes+type, single row.
Enablement (env overrides):
= force off, non-zero = force on
~neutral vs mmvq)
Default is on only for RDNA3.5; every other arch keeps mmvq.
Full decode comparison (38 models)
Mean +3.09%, median +2.78% (n=38). * = controlled DQ-toggle run (DQ-off/mmvq
vs DQ-on/mmvdq) substituted for the three thermally-throttled full-suite
rows.