Conversation
Skip the nb[3] check when ne[3] == 1, the shader never reads it for a single stream. Cache views carry the full-buffer stride there, so the old check reduced to n_kv == kv_size and the path only engaged with the cache full.
* CUDA: Allow CUDA optimization per split for multi-GPU. Previous guard caused multi-GPU to skip the graph optimization. The graph is already split per device and the optimization doesnt run over the whole model but once per split, and thus should be allowed. However, the CUDA event ggml_cuda_concurrent_event belongs to whichever GPU was "current" when created. If the pass ran while GPU 0 was current, it would stick and during event creation for the second GPU it would land on GPU 0. The fix: set the device explicitly ggml_cuda_set_device(cuda_ctx->device); Default behaviour remains unchanged, only active for GGML_CUDA_GRAPH_OPT=1. Explicit device setting pattern re-used from ggml_backend_cuda_graph_compute. * Update ggml/src/ggml-cuda/ggml-cuda.cu Co-authored-by: Aman Gupta <amangupta052@gmail.com> --------- Co-authored-by: tannerbruhn <tannerbruhn@users.noreply.github.com> Co-authored-by: Aman Gupta <amangupta052@gmail.com>
* mtmd : mark context as const in more methods Mark `mtmd_context` as `const` in: - mtmd_bitmap_init_lazy - mtmd_tokenize - mtmd_tokenize_from_parts - mtmd_helper_support_video - mtmd_helper_bitmap_init_from_file - mtmd_helper_bitmap_init_from_buf - mtmd_helper_video_init - mtmd_helper_video_init_from_buf - mtmd_helper_model_can_chat The tokenization functions in particular are useful to have marked `const`, as that allows more easily telling the compiler that we can safely tokenize from multiple threads (`mtmd_tokenize` is already documented as thread-safe, this just reifies that in the signature). * mtmd : mark tokenization input pointer as const Mark the `bitmaps` and `parts` pointers in `mtmd_tokenize` and `mtmd_tokenize_from_parts` as `const`. This allows more easily calling these with immutable arrays / vectors. * mtmd : mark llama_context as const in mtmd_helper_model_can_chat
* metal : fix glu dispatch with ne00 = 1 * tests : disable ill-defined tests
* metal : support n_kv_max sparse mask hint in flash attention vec kernel
- add kernel_flash_attn_ext_vec_idx: compacts finite mask entries into
a per-row index list (Hillis-Steele scan, one threadgroup per row)
- extend vec FA kernel with optional sparse index gathering (FC slot 5)
- add host-side gate: sparse path when n_kv_max > 0, mask present,
supported head sizes / KV types, n_kv_max <= 4096
- new buffer region extra_idx for the index list
- pipeline getter extended with has_sparse param
- add test cases: head sizes, quant types, nb>1, nr23 variants,
sinks, ALiBi, softcap, permute, v_view_of_k, no-mask fallback
Note: multi-row (nb*nr23[1] > 1) cases still failing - rid mapping
in the store phase needs revisiting for the sparse path.
Assisted-by: pi:llama.cpp/Qwen3.8-27B
* metal : fix sparse flash attention row addressing
- kernel_flash_attn_ext_vec_idx: mask param is half* but nb31 is a byte
stride, so the per-row mask offset was scaled by 2x; cast to char*
before applying the byte strides
- kernel_flash_attn_ext_vec: sparse pidx param is char* so the per-row
element offset was under-scaled by sizeof(int); scale it by sizeof(int)
to get the correct byte offset
- fixes the multi-row (nb*nr23[1] > 1) sparse flash attention failures
Assisted-by: pi:llama.cpp/DeepSeek-v4-0731
* cont : use sparse vec FA for prefill
* metal : single-pass flash attention sparse index compaction
The idx kernel previously read the mask row twice: once to count the finite
entries (for the prefix scan) and again to recover their positions. Since the
kernel is memory-bound, this doubled the mask traffic.
Keep the finite positions in a per-thread register array during the count
pass and write them out directly, avoiding the second mask read. A dense
mask with more than NLOCAL finite entries in a slice falls back to re-reading
the mask to write the remaining positions.
Assisted-by: pi:llama.cpp/DeepSeek-v4-0731
* tests : add perf cases for sparse flash attention prefill
Measure the sparse vec FA kernel across KV sizes, n_kv_max hints and batch
sizes. Run with:
./build/bin/test-backend-ops -b MTL0 -o FLASH_ATTN_EXT -p "n_kv_max=[1-9]" perf
Assisted-by: pi:llama.cpp/DeepSeek-v4-0731
* qwen4 : enable sparse attention
* cont : adjust nsg
* cont : sync test-backend-ops
* cont : disable Qwen4 for now
* cont : clean-up + tests
* server : use pytest-xdist for server tests This commit adds pytest-xdist to the server tests. This is pytest plugin that distributes test execution across multiple CPU cores. Assisted-by: pi:llama.cpp/qwen3.8-27B Refs: ggml-org#26734 (comment) * remove server_base_port and BASE_PORT * use worksteal and pytest builting tmp_path
* snapdragon: update CI script to use new snapdragon/run.py * snapdragon: update build.py to not set +x on /lib
…ns (speculative decoding/MTP) (ggml-org#26477) * opencl: quant lm_head / decode GEMV and medium-batch GEMM optimizations * opencl: guard q4_K/q6_K tiled_ns convert-kernel registration for non-Adreno build * opencl: gate q4_K MUL_MAT+GLU fusion dispatch to Adreno * opencl: require the noshuffle weight layout in the q4_K GLU fusion gate * opencl: do not take the vectorized f16 mrow GEMV path on an unaligned row stride * opencl: pass the new get_scale_min_k4 stride argument at the row-major call sites * opencl: enable the q4_K split-K decode GEMV only where it is measured to win * opencl: record the X1-85 split-K datapoint (neutral, exclusion confirmed) * opencl: restrict the tiled lm_head/embed GEMV default to X2E/A8X * opencl: fix q4_K variant kernels to read the transposed scales layout * opencl: keep the flat-GEMV large-m escape opt-in * opencl: guard the o4 GEMV store against the rounded-up dispatch tail * opencl: restore the tiled q4_K/q6_K layout on tensor read-back * opencl: split-K for the q8_0 decode GEMV at small M * opencl: keep the q6_K noshuffle correctness escape ahead of the opt-in gate
Fuse RMS_NORM+MUL+ADD and ADD+ADD under GGML_SYCL_ENABLE_FUSION. ADD+ADD uses the same binbcast indexing and type matrix as standalone add() (f32, f16, f16/f32, i32, i16, bf16, including broadcast and non-contiguous). Unsupported combinations fall back to two add() launches.
* src : add n_expert_used_max function With Commit c61b98b ("model: add NVIDIA Nemotron-3-Puzzle-75B-A9B (NemotronHPuzzle) support (ggml-org#25444)") it is now possible for each layer to have a specific number of experts but there are a few checks that need to be updated to handle this upon model loading. For example: ```console llama_model_load: error loading model: model has expert layers but no expert layers are used ``` And later: ```console /llama.cpp/src/llama-model-loader.cpp:955: GGML_ASSERT(n_ids_used > 0) failed ``` This commit adds the n_expert_used_max function so that these checks can use it. Refs: ggml-org#25444 (comment) * src : use hparams.n_expert_used_max in llama_model_base::load_hparams * src : use 0 as initial value for n_expert_used_max
Signed-off-by: Aaron Teo <aaron.teo1@ibm.com>
Let llama_print_build_info write to a caller-provided FILE* instead of hardcoding stderr. The parameter defaults to stderr so existing callers keep their current behavior. The version command in llama-app now passes stdout, so plain version output goes to stdout where users expect it. Signed-off-by: Adrien Gallouët <angt@huggingface.co>
Signed-off-by: Adrien Gallouët <angt@huggingface.co>
…8271) Use std::error_code overloads of fs::current_path() and fs::directory_iterator in ggml_backend_load_best() so an inaccessible search path (WebDAV mount, removed CWD) is skipped instead of terminating the process with an uncaught filesystem_error. Signed-off-by: Adrien Gallouët <angt@huggingface.co>
This commit adds a cmake version configuration file to replace the current compile definition solution for the version. The motivation for this change is that I made a mistake and did not take into consideration that the compile definition means that this will become a compiler flag for all sources in the target. This means that when a version update happens that will recompile all sources in the target even if they have not changed. Refs: ggml-org#28278
* model, mtmd: fix gemma4 vision handling * nits
* ggml : rename and make private ggml_op_alloc_size_may_expand() (ggml/0) cont ggml-org#27960 * ggml : bump version to 0.23.0 (ggml/1618) * sync : ggml
Defer queued requests when resident-ctx-sum + candidate prompt would reach the threshold; deferred tasks retry on slot release. Auto-slot path only (explicit id_slot bypasses). Vanilla slot accounting: max(prompt-cache tokens, full task prompt length) per processing slot. Env: LLAMA_ARG_PARALLEL_CTX_THRESHOLD. Co-Authored-By: opencode <noreply@opencode.ai>
Mirror the defer SRV_INF with an admit line (resident + candidate < threshold) so boundary tests can show the exact accounting either way. Co-Authored-By: opencode <noreply@opencode.ai>
… exceeding threshold alone The gate deferred any request with resident + candidate >= threshold, including resident == 0. A lone request bigger than the threshold on an otherwise idle pool then defers forever: nothing is resident, so no slot release ever retries it. The threshold guards combined oversubscription between concurrent requests, not a single request's own size (that is bounded by the per-slot cap). Defer now requires resident > 0; a lone request always admits. Admit log distinguishes the lone case so the printed comparison stays accurate. Co-Authored-By: opencode <noreply@opencode.ai>
… one task A release event re-posted a single deferred task (FIFO head, or one explicitly requesting the slot). If that task re-deferred - e.g. the hydra#747 threshold gate deferring a large candidate - the release was burned and every other waiter stayed queued even with a slot idle, while brand-new arrivals kept getting served from the main queue (priority inversion, silent client hangs). Re-post the entire deferred FIFO in order (explicit-slot matches first) to the front of the main queue so every waiter is re-evaluated against the freed capacity before newer arrivals; tasks that still cannot proceed re-defer to the back, preserving FIFO order. Deterministic repro + production case: hydra#747 task-12239 hang (docs/investigations/740-results-report.md). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Assisted-by: Codex
…rowing Port upstream danielhanchen/llama.cpp qwen4exp/mtp: draft MTP block tensors (hc_head_norm/down/up), DECODER_MTP graph, ctx_other borrowing for shared-embd heads, hybrid MTP memory, gemma4-only mem-share gate. Assisted-by: Muse Spark (opencode)
Add --rpc-port flag to llama-server to expose a binary TCP listener that speaks the Hydra RPC protocol (see specs/rpc-protocol.md). New ops (0x30-0x32): STATE_GET (0x30) — serialize slot KV state, send as response payload STATE_PUT (0x31) — receive KV state bytes, restore to slot STATE_META (0x32) — slot metadata only (n_past, state_size, is_processing) Key design: - llama-server knows nothing about Store or sessions - key = slot_id as ASCII string; persistent TCP connections - Agent pipelines llama RPC socket → Store RPC socket (no 2nd 800 MB buffer) - HTTP /slots/:id/state/meta added as debug/curl fallback - Thread safety: is_processing() guard for M0; TODO task-queue in M1 Files changed: tools/server/server-rpc.h NEW — wire-format constants (MAGIC, op/status codes) tools/server/server-context.h add start_rpc_server(int port) declaration tools/server/server-context.cpp add hydra_* helpers + server_context::start_rpc_server tools/server/server-http.h add put() method declaration tools/server/server-http.cpp implement put() mirroring post() tools/server/server.cpp call start_rpc_server after model load; add HTTP meta route common/common.h add rpc_port = 0 to common_params common/arg.cpp add --rpc-port / LLAMA_ARG_RPC_PORT Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…+ split-cuda script Cherry-pick -m 1 of b933c30. Applied: socket_t::connection_id() (transport.cpp/h) + scripts/build-split-cuda.sh. Skipped: ggml-rpc.cpp hunks (sock-based resolve_bound_socket, stale remote_ptr guards, alloc-size fallback). N/A on this line: cdd1102 uses the dispatcher model and lacks the hydra-fork get_socket / re-resolve infra the PR side assumes (same verdict as prior port: rpc guard N/A). Keeping HEAD dispatcher code avoids a duplicate get_device_memory definition and ~15 dangling symbol refs.
…gguf-py) Port upstream ggml-org#28243 shared-tensor borrowing modules onto baseline cdd1102. C++ graph side (speculative.cpp, llama-arch, llama-context, llama-model.h, models.h, qwen4exp.cpp, llama-model.cpp hybrid gate) already matches upstream via local port cdd1102; this commit adds the 11 missing files: - conversion/{base,qwen,qwen4exp,bailingmoe3,command_r,dots3,glm}.py: mtp_shared_embd flag, qwen4exp MTP export (eh_proj fusion, mixer rename, dense compress_ratio trailer, mtp_only PLE skip) - convert_hf_to_gguf.py: --mtp-shared-embd flag - gguf-py/gguf/{constants,tensor_mapping}.py: NEXTN_HC_HEAD_* registration + QWEN4EXP NextN tensor list - src/llama-model-loader.h: whitespace sync with upstream SKIP per brief: overlap/priors/early-router/moe_grouped. No merge; leader merges.
- New tools/llama-engine/ target: thin exe linking server-context + llama-common + llama - include/llama-hydra.h + src/llama-hydra.cpp: Hydra-specific APIs isolated from upstream - llama_state_seq_get_data_to_fd: zero-copy KV state streaming to socket - llama_hydra_peer_reachable: TCP connect test for graceful degradation - Rename --rpc to --rpc-engine (keep --rpc as hidden alias for one release) - Graceful degradation: engine starts in SOLO mode if peer unreachable - Build verified (CPU-only, no warnings) Part of ggml-org#161 (Epic: phase-optimized inference engine) Co-Authored-By: opencode <noreply@opencode.ai>
Adapt llama-hydra to_fd wrapper to get_size+get_data+send instead of 69d49e0 zero-copy path; header comment now states the dropped zero-copy and why (avoid async machinery). Co-Authored-By: opencode <noreply@opencode.ai>
#44) server_context::start_rpc_server() declared hydra_rpc_ctx as an automatic-storage local, then handed hydra_rpc::start() its address to retain in a process-lifetime singleton (hydra_rpc::state().hydra_ctx), read by every future RPC connection from a bounded-thread-pool worker thread. The local went out of scope the moment start_rpc_server() returned, so the singleton retained a dangling pointer into a freed stack frame — a stack-use-after-return. Whether a given request's queue_tasks/queue_results dereference through it "worked" depended entirely on whether the freed stack slot had been reused yet, which explains the intermittent nature of #43 (task processed, result queued, but the response body never reaching the client socket). Fix: give ctx static storage duration. start_rpc_server() only ever runs meaningfully once per process (hydra_rpc::start() itself guards double-start), so `static` gives it exactly the lifetime the singleton already assumes. Also make hydra_send_all/hydra_recv_all log on failure (fd, bytes transferred, errno) instead of silently returning false — every caller already treats a false return as "give up" but had no way to tell a real socket error from `-lv` output, which the issue itself flagged as a diagnostic gap. Verified live on hardware (RTX 5060 Ti, Qwen3.5-2B-Q8_0): INFO (0x41) and CONFIGURE (0x40) previously timed out after 10s against a raw socat client, a raw Python client, and C# RpcClient. All three now round-trip correctly, including a second request on the same persistent connection. test-hydra-rpc-bind, test-hydra-configure-tier, and dotnet Tests.Shared EngineOpcodeTests all still pass. Closes #43 Co-authored-by: Hydra Engineering <hydra-engineering@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…#451 Port of 860101b adapted to this lineage: progress fields (operation/progress/tokens_processed/tokens_total/elapsed_ms) reported by hydra_handle_state_meta from server_slot state/stats. M1 task-queue parts of the original (hydra_state result structs, PREFILL metrics, get_state route) skipped - no M1 base in this tree by design (pick 1b option B). Original: 860101b feat: expose slot progress/status for ggml-org#451 (#55)
Port of f7c8be1. Its checkpoint-before-decode core targets the M1 manual PREFILL batch loop (opcode 0x42), which does not exist in this tree - our prefill path is upstream update_slots, which already creates the checkpoint before llama_decode. No manual PREFILL loop, no hydra_rpc singleton, no set_routes_ptr base: those hunks skipped. Kept the portable prerequisite: guarded hydra_rpc.cpp link + include dir in tools/server/CMakeLists.txt (empty-safe EXISTS guard; no hydra_rpc dir in this tree yet). Original: f7c8be1 fix(hydra): honor checkpoint-before-decode invariant
slot.n_prompt_tokens_cache / slot.n_decoded do not exist on server_slot in this lineage (pre-existing breakage since 79ebdf4cf - the hydra RPC handlers never compiled). Use the stats equivalents the rest of this tree uses (slot.stats.n_prompt_cached / slot.stats.n_gen, cf. the n_decoded/n_prompt_tokens_cache task-result mappings). Found by CPU-only build-check of the 1-series tip (llama + server-context); both targets now build clean. Co-Authored-By: opencode <noreply@opencode.ai>
Upstream hc-ops (ggml-org#28901, in rebase base) moved the HC grouped-norm gamma to [n_embd, hc]; the shared-draft file still carries flat hc_dim gammas. The layer-norm declarations were already ported with ALLOW_RESHAPE during the rebase, but NEXTN_HC_HEAD_NORM was missed: the draft's own blk.*.nextn.hc_head_norm stayed flat and aborted graph_mtp in build_hc_mix (ggml_can_repeat fail, A=[2560,4,4] vs B=[10240]). Same reshape-tolerant treatment; verified by strict-config leg on the rebased tree.
Unmarked intermediates can have buffers recycled before extraction runs, yielding stale contents instead of ids. Stash per-layer selected_experts and set output when HYDRA_TRACE_ROUTES or HYDRA_PIN_FILE requests them. Off by default. Assisted-by: Muse Spark
As-is snapshot of the compact-gather pilot for architect review. Known pending: Finding-1 rework (device-side slot_of, no per-call host readback/sync). No completion claimed; closes nothing. Assisted-by: Muse Spark
|
HEADLINE FINDING (architect ruling, banked §19 / commit c5e078d97): the gather hook is installed on the MMVQ dispatch branch, but decode dispatches to MMQ (engagement counters: mmvq=0, mmq=2192, hit=0, miss=0 — zero gather engagements; fused path bypasses the thunk hook). The mechanism as wired is a decode-time no-op; armed tok/s 17.80/17.85 vs 17.7476 same-session anchor confirms stock behaviour. Any review of this surface should treat hook-dispatch placement (MMQ vs MMVQ) as the primary defect. ARM 006 armed-traffic measurement on this build is VOID (no-op), and the related PCIe premise test has been withdrawn as archaeology on a superseded mechanism — see track bank §18/§19 for the CPU-bound pivot (compute/backend split per expert, not weight-address selection). |
Architect review — gather-MMVQ pilotReviewed at The number that governs this reviewThe expert-ranking prize is 5.23 ms/token (12.44 ms/token per 100% of expert work relocated CPU→GPU,
Every finding below is stated against that budget. B1 — Design: misses are shipped to the GPU instead of computed where their weights live (BLOCKING)
Cost, using the measured 0.597 MiB per expert per site: The design pays for GPU compute by shipping weights across the bus, and on this rig the bus is the Solution — not a patch, an architecture change. Never move the weights; move the activations. Hits B2 — Per-call full stream sync (BLOCKING)
CUDA_CHECK(cudaMemcpyAsync(ids_host.data(), ids->data, n_ids*sizeof(int32_t), cudaMemcpyDeviceToHost, stream));
CUDA_CHECK(cudaStreamSynchronize(stream));~144 host syncs per token, at 10–50 µs each — at or over break-even before any useful work happens. Solution: determine the used-expert set on-device. The ids are already on the GPU; a small M1 — The sync is uncounted and untimed on the path that actually runs (MEASUREMENT)
Solution: time every invocation of the engaged path and report p50/p99 µs per site, not just totals. M2 — Two different mechanisms share one set of hit counters (MEASUREMENT)Two hydra paths coexist with different semantics:
Both increment the same counters, so any run engaging both reports an Solution: one counter set per path, tagged; count lookups not unique rows; label the decode-only scope M3 — Pin-file loading is silent on every failure (MEASUREMENT, most important for trust)
This makes "the mechanism ran and did nothing" indistinguishable from "the mechanism never engaged," which Solution: hard-fail on R1 — The VRAM slab is invisible to the fit-check and never freed (RESOURCE)
At N=38: Solution: allocate through a ggml backend buffer so the memory is accounted, and free it with the R2 — Duplicate and out-of-range pins over-allocate silently (RESOURCE)
Solution: deduplicate and bounds-check at load time, and size the slab from the validated set. N1 —
|
DRAFT review surface for the compact-gather pilot, exactly as it stands. Not a completion claim. Closes nothing.
Measurement context (ARMs)
Scope
ggml/src/ggml-cuda/ggml-cuda.cu: dispatch-time compact-gather pilot (+347)src/llama-context.cpp/.h: pin plumbing (+189/+2)src/llama-graph.cpp: hook site (+1)Known pending: Finding-1 rework (device-side slot_of, in-kernel row-address select; no per-call host readback/sync). Code fixes come after review, not before.