Tiered kv cache implementation along with vattention (mad-lab-cache)#8
Merged
Conversation
- Fix Issue 1: Replace CUDA API calls with HIP API in ggml-cuda.cu - cudaMemcpyPeerAsync → hipMemcpyPeerAsync - cudaError_t → hipError_t - cudaSuccess → hipSuccess - Fix Issue 2: Fix typo std.max → std::max in llama-eviction-policy.h - Fix Issue 3: Implement migrate_tokens and batch_migrate_tokens with HIP-aware memory copies - Fix Issue 4: Implement save_to_ssd and load_from_ssd using llama_ssd_storage_format - Fix Issue 5: Add ggml tensor handles (ggml_tensor*) for K and V data to tier class - Fix Issue 6: Add device-to-host copy for hot→warm path before serialization - Added is_device_data parameter to save_to_ssd() - Added to_device parameter to load_from_ssd() - Uses hipMemcpy with hipMemcpyDeviceToHost/hipMemcpyHostToDevice as needed
- llama-io-uring: async fixed-buffer read class wrapping liburing; auto-detected at cmake configure time on Linux; falls back gracefully to pread if io_uring_queue_init fails - llama-model-loader: replaces read_raw_unsafe with io_uring submit+wait in the weight-upload loop; achieves ~5.7 GB/s on SN850X vs 1.5 GB/s baseline via SAM pipeline - llama-kv-cache-tiered: warm_device field in llama_tier_config; when set, init() hipMalloc warm K+V buffers on that device; hot->warm migrations copy R9700 VRAM -> host staging -> 6900XT VRAM instead of SSD; spills to SSD only when warm tier is full
Adds --kv-warm-device <N> flag (env: LLAMA_ARG_KV_WARM_DEVICE) to specify a HIP device index for the warm KV cache tier. Wires through common_params into llama_tier_config.warm_device and calls set_warm_elem_bytes() before init() so hipMalloc sizing is correct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ops.cpp: remove redundant extern in extern-C decl (turbo-quant merge issue) - src/CMakeLists.txt: add llama-kv-cache-tiered.cpp to llama lib + HIP includes - tools/server/CMakeLists.txt: add /opt/rocm/include + __HIP_PLATFORM_AMD__ - server-tiered-cache.h: add default ctor; fix global_stats/stats_ name collision - server-tiered-cache.cpp: update stats_ references after rename - server-context.cpp: fix unique_ptr copy assignment; fix tiered_cache scope in metrics lambda; fix n_discard ordering; fix SLT_INF variadic macro; fix json::object push_back for ordered_json; add friend server_routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The tiered cache percentages define hard capacity limits per tier that sum to the full ctx budget. Previously the R9700 KV buffer was still allocated at full n_ctx despite the hot tier being only a fraction. - Add kv_tier_total_ctx to common_params to preserve the full budget - Before common_init_from_params, reduce n_ctx to hot_pct * n_ctx so the GPU KV buffer only allocates for the hot tier - server-tiered-cache.cpp uses kv_tier_total_ctx for config.total_ctx so warm/cold capacity calculations remain correct Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace placeholder warm tier (flat single-buffer) with per-layer TieredKVLayer structs. Add public get_layer_k_raw/v_raw/get_num_kv_layers/is_v_transposed accessors to llama_kv_cache. Wire set_kv_layers_from_cache() from server init_slot so tensor pointers are populated at startup. Hot→warm migration now: - Copies K contiguously via hipMemcpy (VRAM→RAM or VRAM→eGPU D2D) - Gathers transposed V column at pos via hipMemcpy2D (VRAM→RAM) or element loop - Warm→hot restores with scatter for v_trans=true (hipMemcpy2D) Metadata-only fallback retained when set_kv_layers_from_cache() not called. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s 1-4) Phase 1-2: Real per-layer K/V tensor data movement via hipMemcpy/hipMemcpy2D. - hot->warm: K contiguous copy, V transposed gather (hipMemcpy2D) - warm->hot: K direct copy, V scatter back (hipMemcpy2D) - TieredKVLayer struct with per-layer tensor pointers and warm buffers - set_kv_layers_from_cache() wired from server init Phase 3: io_uring cold tier for NVMe direct I/O. - liburing integrated for async cold storage reads - SSD path for evicted KV chunks Phase 4: Semantic embedding-driven prefetch. - bge-small (33M) fingerprints evicted KV chunks - Cosine similarity prefetch hints on new prompt arrival - Two-stage cold->warm->hot promotion for semantic cache hits - semantic_top_k and prefetch hint routing fully wired Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…abled Hot tier shrinks n_ctx passed to llama init, so llama_n_ctx_seq() returns the hot tier size (~60% of --ctx-size). With tiering, slot.n_ctx must equal kv_tier_total_ctx so requests up to the full context window are accepted.
O_DIRECT on a 100GB model file during the metadata-only scan hangs. The dry-run only needs metadata so normal buffered IO is fine.
hipHostMalloc buffers are already pinned by the HIP driver so io_uring_register_buffers fails with ENOMEM on ROCm. Fall back to unregistered io_uring reads rather than abandoning the ring entirely.
With --direct-io on ROCm+SAM, allocate staging buffers using hipExtMallocWithFlags(hipDeviceMallocFinegrained) instead of hipHostMalloc. Fine-grained VRAM is CPU-accessible via BAR1, so io_uring writes land directly in VRAM over PCIe without touching system RAM. Falls back to pinned host memory if hipExtMallocWithFlags fails.
…aults After context shift, slot.prompt.checkpoints retained pre-shift positions. Subsequent checkpoint restore via llama_state_seq_set_data_ext used stale positions causing GPU memory access faults on multi-GPU ROCm setups.
- llama-weight-pager: page table, VRAM slot pool, LRU eviction - page_in via pread into hipExtMallocWithFlags fine-grained VRAM (SAM path) - ggml eval callback hook: redirects tensor data pointers before each op - async io_uring prefetch: pipelines next layer NVMe read with GPU compute - CLI: --weight-paging, --weight-paging-slots, --weight-paging-prefetch - metrics: weight pager stats exposed at /metrics endpoint Fixes: hipMalloc pool alloc, strcmp tensor name comparison, slot eviction page invalidation, llama-model.h include for server-context.
…ama.cpp into feature-kv-cache-improvements
…d model support - Fix warm GPU tier OOM: skip RAM staging buffers when warm_device >= 0 (6900XT); previously allocated both RAM and GPU warm buffers, wasting ~8GB RAM per session - Fix k_bytes/v_bytes computation: use nb[1] (row stride) instead of ne[0]*element_size which returns block size for quantized types like turbo4, causing 557056 MiB warm GPU misreads and silent hipMalloc failures - Fix hybrid model KV wiring: handle llama_memory_hybrid and llama_memory_hybrid_iswa in init_slot so Qwen3-27B and similar hybrid models wire attention KV layers into the tiered cache instead of metadata-only mode - Add tier logging: warm GPU (ROCm device + MiB), cold SSD (slots + path) on init - Weight pager: use ggml_backend_buft_alloc_buffer for pool allocation so tensor->buffer is valid on callback; set tensor->buffer = pool.ggml_buf on page-in - Weight pager: disable async prefetch default (io_uring page-index tracking broken); move fd dup outside prefetch gate so pread always has valid fds - Weight pager: clear O_DIRECT on dup fds; zero padding bytes after hipMemcpy - hipGraph: disable via setenv + remove static from disable_cuda_graphs_due_to_env Tested on AMD Radeon AI PRO R9700 (gfx1201, 32GB) + RX 6900XT (16GB warm tier) with Qwen3.6-27B-Q6_K (tiered KV), Qwen3.6-35B-A3B, Qwen3.5-REAP-97B, and MiniMax-M2.7-229B-IQ3_S (NVMe demand weight paging, 8-slot VRAM pool). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
AMD ROCm Tiered KV Cache + NVMe Demand Weight Paging
Overview
This PR implements and stabilizes two major infrastructure features for running large models on AMD ROCm hardware with limited VRAM:
Both features are WIP infrastructure. Output correctness for weight paging is a known follow-up item.
Tiered KV Cache Fixes
Warm GPU tier OOM fix
When
--kv-warm-device Nis set, the pool previously allocated both RAM staging buffers and GPU VRAM buffers for all warm slots. On a 262144-slot warm tier this caused ~8GB of wasted RAM and an OOM kill. RAM staging buffers are now skipped when a GPU warm device is configured.k_bytes/v_bytes quantization fix
Per-token KV byte size was computed as
ne[0] * ggml_element_size(tensor). For block-quantized types (turbo4, Q4_K, etc.)ggml_element_sizereturns the block size in bytes rather than bytes-per-element, producing a 128× overestimate. Switched totensor->nb[1](actual row stride), which is correct for all types.Hybrid model KV wiring
init_slotcastllama_get_memory()tollama_kv_cache *. Hybrid-architecture models (Qwen3-27B and similar) returnllama_memory_hybridorllama_memory_hybrid_iswa, causing silent metadata-only fallback. Added casts for both hybrid types so attention KV layers wire correctly into the tiered cache.Tier init logging
Added startup log lines for warm GPU (
ROCm%d KV warm buffer size = X MiB) and cold SSD (cold tier: N slots reserved, path: ...) to match the existing hot tier output.Weight Pager Fixes
hipMallocwithggml_backend_buft_alloc_buffer; setstensor->buffer = pool.ggml_bufon page-in soggml_cuda_mul_matdoesn't crash onsrc->buffer->buftdereferenceio_uringpage-index tracking incomplete_prefetchis broken; disabled until fixedpage_inusespreadregardless of prefetch state; fds must always be populatedpage.sizeup toslot_sizeso quantized kernel reads past tensor data are safeGGML_CUDA_DISABLE_GRAPHS=1via setenv; removedstaticfromdisable_cuda_graphs_due_to_envincommon.cuhKnown Issues / Follow-ups
tensor->extra; callback overridesdataandbufferbut notextra; GPU kernel reads staleextra→ coherent but wrong tokens. Fix: pre-seat paged tensors intopool.ggml_bufbeforeggml_backend_sched_alloc_graph.complete_prefetch/submit_prefetchHardware / Test Config
--kv-warm-device 1)Models Tested
Co-authored with Claude Sonnet 4.6