What You'll Build
A llama-server endpoint running Nanbeige4.2-3B on an RX 7800 XT with a 98,304-token window and near-lossless quantization on both axes — Q8_0 weights and a q8_0 KV cache — for 13.588 GiB accounted inside a card that has 16 GiB to give, on a HIP build of llama.cpp targeting gfx1101.
Hardware data: RX 7800 XT (16 GB VRAM) · Q8_0 weights 4.130 GiB + q8_0 KV at 98,304 tokens 8.766 GiB + reserved logits 0.317 GiB + FlashAttention dequant scratch 0.375 GiB = 13.588 GiB derived · no benchmark submitted yet · See benchmark data
⚠️ The 3B's weights are not the constraint here — the KV cache is. Nanbeige4.2-3B is a Looped Transformer: the model card says its "Looped Transformer architecture reuses the transformer layers to increase model capacity without adding parameters."
config.jsonsetsnum_loops: 2, so its 22 blocks execute twice per forward pass over one shared set of weights — and each pass keeps its own keys and values. llama.cpp therefore allocates KV for 44 logical layers, and every context figure you would compute fromnum_hidden_layers: 22is exactly half the truth.
ℹ️ This recipe is in two halves and they fail independently. The platform half — ROCm, the HIP build, which llama.cpp flags mean what on this backend — is common to RDNA3 Radeons and needs only the target string changed. The memory half is arithmetic against 16 GB and shares nothing with the 24 GB Radeon page beyond the per-token rates. In particular, this card does not reach the model's declared 262,144-token context in VRAM, and the section below shows why rather than asserting it.
Requirements
| Component | Minimum | This recipe |
|---|---|---|
| GPU | 16 GB VRAM, ROCm-supported AMD GPU | — not measured; the budget below is derived from file bytes and llama.cpp's allocation rules (/contribute) |
| RAM | 8 GB system RAM | — |
| Storage | 4.43 GB for the Q8_0 GGUF (decimal, as HuggingFace lists it) | 4,434,787,488 bytes from the HF tree API; ~15 GB more if you also clone and build llama.cpp against ROCm |
| Software | Linux, ROCm ≥ 6.1, llama.cpp b10153+, CMake | — |
The gfx target is per-board, and this board is not gfx1100
Everything in the install hangs off one string, and it is the single thing you cannot copy from a 7900-series recipe. AMD's ROCm install-on-linux system-requirements matrix has one row per board with its LLVM target and support status, and the row reads:
| GPU | Architecture | LLVM target | Support |
|---|---|---|---|
| AMD Radeon RX 7800 XT | RDNA3 | gfx1101 | ✅ |
The RX 7900 XTX, XT and GRE are gfx1100 on the same page; gfx1101 covers this card together with the RX 7700 XT, the RX 7700, the Radeon PRO W7700 and the PRO V710. Both are RDNA3, so everything about the compute path below is shared — but the compiler needs the right one.
Two consequences before you type anything.
- Read the target off AMD's per-board matrix, not off a series-level compatibility page. llama.cpp's build guide links the LLVM AMDGPU processor list for target ids, and that table does agree — its
gfx1101row names the Radeon RX 7800 XT, RX 7700 XT and RX 7700 in its own product cell. It is a compiler-backend reference, though, not a support matrix: its board column is headed "Example Products", its OS-support column defers explicitly to AMD's runtime release notes, and thegfx1101cell in that column is empty. Confirm a target id against it if you like; do not conclude anything about ROCm support from it. - This board's supported-OS list is shorter than the page's general list. The matrix footnotes the consumer Radeon rows, including this one, with: "only support Ubuntu 24.04.4, Ubuntu 22.04.5, RHEL 10.1, and RHEL 9.7." The distribution table further down that page is broader than what applies to your card. Read the footnote attached to your row.
- Do not set
HSA_OVERRIDE_GFX_VERSION. That variable exists to make an unsupported card masquerade as a supported one;docs/build.mdintroduces it with "If your GPU is not officially supported". gfx1101 is officially supported, so setting it can only mislead the runtime.
Confirm your own card rather than trusting this paragraph:
rocminfo | grep gfx | head -1 | awk '{print $2}'
# expect: gfx1101
The KV cache doubles, and that is not a bug
1. The vendor designed it that way. config.json carries "num_loops": 2 alongside "num_hidden_layers": 22. A Nanbeige team member, replying on the model's own discussion #18 (leran1995, flagged as an org member): "we did try sharing the KV cache across loop passes, but it noticeably hurt performance, so we kept the full cache in Nanbeige4.2". Cache sharing was built, measured and rejected — the doubling is the design, not a llama.cpp artefact.
2. llama.cpp implements it that way. src/models/nanbeige.cpp expands the logical layer count before anything is allocated — hparams.n_layer_all = (uint32_t) ((size_t) n_layer_phys * (size_t) n_loops); under a comment reading "Expand logical layer count before load_tensors() allocates layers / KV." — then aliases the layer structs across loops under a second comment, "Share physical weights across loops; each slot still has its own KV index." src/llama-kv-cache.cpp sizes the cache with const uint32_t n_layer = hparams.n_layer_all;. For nanbeige with num_loops: 2 that is 44, not 22.
3. Your file has to carry the flag. The loader reads the key as optional with a default of 1 — uint32_t n_loops_u = 1; ml.get_key(LLM_KV_NUM_LOOPS, n_loops_u, false); — so a GGUF converted by a path that omits it loads happily and runs 22 layers. That is a silently different model, not an error. Check the file you downloaded:
# run from your llama.cpp checkout (see Installation step 2)
pip install -U huggingface_hub numpy
python3 gguf-py/gguf/scripts/gguf_dump.py --no-tensors \
../nanbeige4.2-3b/Nanbeige_Nanbeige4.2-3B-Q8_0.gguf | grep -E "num_loops|block_count"
numpy is not a dependency of huggingface_hub, so install both. Two shorter-looking commands do not substitute: llama-gguf <file> r n prints key names only, and piping llama-cli into grep shows nothing because tools/cli/cli.cpp sets params.verbosity = LOG_LEVEL_ERROR; before the metadata is printed. At runtime, llama_kv_cache prints the layer count in its startup line — confirm 44 layers before trusting anything below.
KV cost per token
config.json gives num_key_value_heads: 8 and head_dim: 128, so each logical layer stores 8 × 128 = 1024 elements for K and 1024 for V per token. Over 44 logical layers that is 90,112 elements per token. Per-element cost comes from the block layouts in ggml/src/ggml-common.h, where QK4_0 and QK8_0 are both 32 values per block:
| Cache type | Bytes per element | Bytes per token |
|---|---|---|
f16 (default) | 2 | 180,224 |
q8_0 | 34/32 = 1.0625 | 95,744 |
q4_0 | 18/32 = 0.5625 | 50,688 |
Multiplied out, this is the whole cache bill:
| Context | f16 KV | q8_0 KV | q4_0 KV |
|---|---|---|---|
| 32,768 | 5.500 GiB | 2.922 GiB | 1.547 GiB |
| 49,152 | 8.250 GiB | 4.383 GiB | 2.320 GiB |
| 65,536 | 11.000 GiB | 5.844 GiB | 3.094 GiB |
| 98,304 | 16.500 GiB | 8.766 GiB | 4.641 GiB |
| 114,688 | 19.250 GiB | 10.227 GiB | 5.414 GiB |
| 131,072 | 22.000 GiB | 11.688 GiB | 6.188 GiB |
| 196,608 | 33.000 GiB | 17.531 GiB | 9.281 GiB |
| 262,144 | 44.000 GiB | 23.375 GiB | 12.375 GiB |
What is not the cache, and how much VRAM to actually budget
Two more terms sit on top of weights + KV. Neither is AMD-specific — both are backend-agnostic code that compiles into the HIP build unchanged — but at 16 GB both matter.
A flat 0.317 GiB of reserved logits. llama.cpp sizes one worst-case graph at startup, and src/llama-context.cpp sets the logit rows that graph must hold to n_outputs_pp = std::min(n_tokens, cparams.n_outputs_max), where n_tokens is itself std::min(cparams.n_ctx, cparams.n_ubatch) and n_outputs_max defaults to n_batch. At stock settings that is 512 rows regardless of -c, each a full f32 distribution over this model's 166,144-token vocabulary:
512 rows × 166,144 vocab × 4 B = 340,262,912 B = 324.5 MiB = 0.317 GiB
It is large only because the vocabulary is, and the dial that moves it is -ub, not -c — so it is the same 0.317 GiB in every row of the ladder below.
4,096 bytes per context token of FlashAttention dequant scratch — but only on a quantized cache. The kernels read f16, so a quantized cache is expanded into scratch VRAM first, and fattn-common.cuh appends that copy to the attention output tensor's own allocation:
if (need_f16_K && K->type != GGML_TYPE_F16) {
data.end = GGML_PAD(data.end, 128);
data.K = data.end;
data.end += ggml_nelements(K)*ggml_type_size(GGML_TYPE_F16);
}
with a matching block for V. This applies on ROCm, because the HIP backend is the same ggml-cuda tree compiled a second time — ggml/src/ggml-hip/CMakeLists.txt globs ../ggml-cuda/*.cu rather than carrying its own kernels. The guard's second conjunct means an f16 cache pays exactly zero: the only place on this page where the default cache is the cheaper one.
Which kernel gets picked decides whether the term is charged, and the answer must come from the reserve graph rather than from what happens at decode. ggml/src/ggml-cuda/fattn.cu sets need_f16_K = true; need_f16_V = true; for the TILE and MMA_F16 kernels; the direct-read vector kernel needs no scratch but is only reachable at Q->ne[1] <= 2. llama.cpp reserves at n_tokens = min(n_ctx, n_ubatch) = 512, which fails that test on every path, and on RDNA3 amd_wmma_available(cc) is true (ggml/src/ggml-cuda/common.cuh defines GGML_CUDA_CC_RDNA3 with the comment "RX 7000, minimum for WMMA", and the macro range covers gfx1101 as well as gfx1100) — so a 512-row reserve takes the WMMA MMA_F16 path and the scratch is budgeted. You cannot dodge this term by shrinking -ub; you dodge it only by not quantizing the cache.
So the per-token price that actually governs a quantized cache is not the number in the table above but that number plus 4,096:
| Cache type | KV bytes/token | + dequant scratch | Effective |
|---|---|---|---|
f16 | 180,224 | 0 | 180,224 |
q8_0 | 95,744 | 4,096 | 99,840 |
q4_0 | 50,688 | 4,096 | 54,784 |
The budget rule for this card: treat 14.5 GiB as the accounted ceiling on 16 GiB. That reserves roughly 1.5 GiB for the HIP context, the per-microbatch activation working set, ROCm's allocation granularity, and the framebuffer if this card also drives your displays — a Radeon in a desktop usually does. None of those is measured on this GPU, which is exactly why the lead configuration below lands at 13.588 GiB instead of pressing the ceiling.
Separately, budget system RAM for the attention mask, which is deliberately absent from every VRAM figure on this page: src/llama-graph.cpp carries GGML_ASSERT(ggml_backend_buffer_is_host(self_kq_mask->buffer));, so it is a host allocation on the HIP backend exactly as on CUDA. At n_kv × n_ubatch × 2 B that is 96.0 MiB at 98,304 tokens and 192.0 MiB at 196,608 — comfortably inside the 8 GB of system RAM the Requirements table asks for. Counting it as VRAM is a common way to over-budget this model.
The 16 GB ladder
Every cell is the largest multiple of 16,384 tokens whose weights + KV + the flat 0.317 GiB logits reservation + the dequant scratch stay under 14.5 GiB accounted. Each cell reads context · GiB accounted, so what a configuration leaves unspent is visible rather than implied. Weight bytes are the bartowski repo's throughout, via the HuggingFace tree API.
| Weights | f16 KV | q8_0 KV | q4_0 KV |
|---|---|---|---|
| Q4_K_M (2.500 GiB) | 65,536 · 13.817 | 114,688 · 13.481 | 212,992 · 13.684 |
| Q5_K_M (2.865 GiB) | 65,536 · 14.182 | 114,688 · 13.846 | 212,992 · 14.049 |
| Q6_K (3.349 GiB) | 49,152 · 11.916 | 114,688 · 14.330 | 196,608 · 13.697 |
| Q8_0 (4.130 GiB) | 49,152 · 12.697 | 98,304 · 13.588 | 196,608 · 14.478 |
The cell that looks like an error is not one. Q6_K with an f16 cache stops at 49,152 and leaves 2.584 GiB unspent, because the next rung — 65,536 tokens of f16 — comes to 14.666 GiB and is over the ceiling; the exact maximum there is 64,549 tokens. One weight rung lighter, Q5_K_M reaches 65,536 with 0.318 GiB to spare. That cliff is what an unquantized cache costs at 180,224 bytes per token.
Two things fall out of the table.
f16 KV becomes affordable at 16 GB — at 65,536 tokens, and only on the two lightest weight rungs. That is worth naming: the untouched default cache is what puts small cards out of this model's game, and 65,536 is exactly the max-new-tokens the model card recommends for agentic and tool-use work. A reader who wants to type -ngl 99 -c 65536 and no cache flags can now do that here.
And you should still not do it. Set the two candidates side by side with every term counted:
| Weights | KV | Logits | Dequant | Total | |
|---|---|---|---|---|---|
Q4_K_M + f16 @ 65,536 | 2.500 | 11.000 | 0.317 | 0.000 | 13.817 GiB |
Q8_0 + q8_0 @ 98,304 | 4.130 | 8.766 | 0.317 | 0.375 | 13.588 GiB |
The quantized-cache configuration wins by 0.229 GiB — nearly a wash. What it actually buys, at the same ~13.6 GiB, is 1.5× the context window and a near-lossless weight tier instead of a 4-bit one. That is a good trade, but it is a trade rather than a windfall, and anyone who budgets q8_0 at its sticker 95,744 bytes per token will be about 0.375 GiB short at this context.
Why the declared 262,144 context is not on this page
The model card says "The model supports a context length of up to 262,144 tokens (256K).", and a 24 GB Radeon holds all of it. This card does not, and the gap is not marginal:
| Configuration at 262,144 ctx | Accounted total | On a 16 GiB card |
|---|---|---|
Q8_0 · q4_0 KV | 17.822 GiB | over capacity by 1.822 GiB |
Q4_K_M · q4_0 KV | 16.192 GiB | over capacity by 0.192 GiB |
Q2_K · q4_0 KV | 15.414 GiB | fits on paper, 0.586 GiB of slack |
The only arithmetic that closes is 2-bit weights with less than 0.6 GiB left for the HIP context and everything else unmeasured — on a model whose pitch is agentic and reasoning accuracy at 3B. This page does not offer it. If you need the whole 262,144 tokens on this card, --no-kv-offload puts the cache in system RAM and keeps the weights on the GPU; that is a real option and a much slower one, and no measurement of it exists on any AMD card.
The long-context alternative that does fit
q4_0 on both axes reaches 196,608 tokens — 75% of the declared context — at 14.478 GiB accounted:
| Configuration | Weights | KV | Logits | FA scratch | Accounted total | Slack on 16 GiB |
|---|---|---|---|---|---|---|
Q8_0 · q8_0 KV · 98,304 ctx (this recipe) | 4.130 | 8.766 | 0.317 | 0.375 | 13.588 GiB | 2.412 GiB |
Q8_0 · q4_0 KV · 196,608 ctx | 4.130 | 9.281 | 0.317 | 0.750 | 14.478 GiB | 1.522 GiB |
Q8_0 · q4_0 KV · 131,072 ctx | 4.130 | 6.188 | 0.317 | 0.500 | 11.135 GiB | 4.865 GiB |
Q8_0 · f16 KV · 49,152 ctx | 4.130 | 8.250 | 0.317 | 0.000 | 12.697 GiB | 3.303 GiB |
That second row sits on the 14.5 GiB ceiling rather than under it, and it drops the K cache to 4 bits. Take it if long context is the point of your workload and the card is not also driving a desktop; take the 131,072 row if it is.
Installation
1. Install ROCm
Follow AMD's ROCm quick start for Linux for your distribution — but check the footnote on your board's row in the system-requirements matrix first, because this card's supported-OS list is a four-entry subset of the page's general table.
The version floor is not advisory. ggml/src/ggml-hip/CMakeLists.txt hard-stops the build:
if (${hip_VERSION} VERSION_LESS 6.1)
message(FATAL_ERROR "At least ROCM/HIP V6.1 is required")
endif()
Verify before you go further:
hipconfig --version
rocminfo | grep gfx | head -1 | awk '{print $2}'
2. Build llama.cpp for gfx1101
Mainline support for this architecture arrived in PR #25994, merged 2026-07-27 as commit b77d646751d01c0962bc203b6809e9d94f7d50b7; release b10153 carries exactly that commit, which is why every source link on this page is pinned to that tag. The model card still tells you to clone the vendor's own nanbeige42 fork; that was correct at release and is not any more.
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
HIPCXX="$(hipconfig -l)/clang" HIP_PATH="$(hipconfig -R)" \
cmake -S . -B build -DGGML_HIP=ON -DGPU_TARGETS=gfx1101 -DCMAKE_BUILD_TYPE=Release \
&& cmake --build build --config Release -- -j 16
That is docs/build.md's own Linux invocation with the target set to this board — the example there ships gfx1030, so the substitution is the whole edit. GPU_TARGETS is optional, and build.md notes that "omitting it will build the code for all GPUs in the current system" — but naming it keeps the compile to one ISA and makes the binary's target explicit.
Note what you are not doing. There is no flash-attn pip step: llama.cpp's FlashAttention is its own HIP kernel set, compiled by the command above, and GGML_CUDA_FA defaults to ON in ggml/CMakeLists.txt. There is no bitsandbytes, no ExLlamaV2 and no FP8 or FP4 weight path — GGUF through this HIP build is the route.
3. Download the weights
Nanbeige publishes no first-party GGUF — enumerating the org returns ten repositories and none carries the gguf tag; its quantized releases are FP8 and GPTQ-Int8. A team member said on discussion #1 that official quantized versions were being prepared, which has not happened yet. Use a community conversion:
pip install -U huggingface_hub
hf download bartowski/Nanbeige_Nanbeige4.2-3B-GGUF \
--include "Nanbeige_Nanbeige4.2-3B-Q8_0.gguf" \
--local-dir ./nanbeige4.2-3b
Check provenance on whichever repo you pick, because this model has a date cliff: mainline support merged on 2026-07-27, hours after the canonical repo landed a tokenizer commit the same day, and a quant is a frozen copy of upstream as of the moment it was uploaded. bartowski's card states the quants were built with llama.cpp release b10159, and the HuggingFace API gives that repo createdAt 2026-07-28T14:38Z — comfortably after both events. owao's repo has an earlier createdAt (2026-07-21T20:33Z) but a lastModified of 2026-07-29T13:06Z, so check the individual file's commit before assuming a given rung there is post-merge. Their bytes are not interchangeable either: owao's Q8_0 is 4,434,787,168 B (320 apart, same to three decimals) but its Q4_K_M is a different file at 2,574,807,904 B against bartowski's 2,684,023,968 B. Every figure on this page is bartowski's; do not mix the two in one sum.
Running
The lead configuration
./build/bin/llama-server \
-m ./nanbeige4.2-3b/Nanbeige_Nanbeige4.2-3B-Q8_0.gguf \
--host 127.0.0.1 --port 8080 \
-ngl 99 \
-c 98304 \
-fa on \
-ctk q8_0 -ctv q8_0 \
--temp 0.6 --top-p 0.95 --top-k 20
13.588 GiB accounted, 2.412 GiB left on the card. Four flags are load-bearing, and it matters which of them are about the runtime rather than the backend, because only the runtime ones are the same advice you would give a CUDA reader:
-c 98304— set it explicitly, and never-c 0. Omitting-cis not the same as passing0: with-cunset, llama.cpp's-fit ondefault (common/fit.cpp) interpolates the context down until the model fits free memory with a margin, so you silently get some arbitrary reduced number rather than the one you budgeted. With-c 0,common/arg.cppsetsparams.fit_params_min_ctx = UINT32_MAX;— a deliberate "give me the full trained context, do not shrink it" — and on this card that means 262,144 cells off16cache, 44.000 GiB, an immediate out-of-memory abort. Backend-independent argument parsing; it reads identically on ROCm.-fa on. A quantized V cache requires FlashAttention;src/llama-context.cppthrowsquantized V cache was requested, but this requires Flash Attentionotherwise. Theautodefault will enable it for you, but being explicit makes the failure mode legible.-ctk q8_0 -ctv q8_0— matched types, and this one really is a backend question, with the same answer. A stock build instantiates FlashAttention kernels only for identical K and V types, and the restriction is not inherited from the CUDA side by accident: the guard infattn.cuis#ifndef GGML_CUDA_FA_ALL_QUANTS/if (K->type != V->type) {/return BEST_FATTN_KERNEL_NONE;with no backend condition on it, andggml/src/ggml-hip/CMakeLists.txtcarries its own copy of the same option, whose default branch compiles exactly four vector instances:f16-f16,q4_0-q4_0,q8_0-q8_0,bf16-bf16. A clever-looking-ctk q8_0 -ctv q4_0needs a rebuild with-DGGML_CUDA_FA_ALL_QUANTS=ON— the HIP build file reads the same variable name, so the escape hatch transfers too.- Leave
--parallelalone.llama-serverdefaults it to-1, andtools/server/server.cppresolves the sentinel withparams.n_parallel = 4;andparams.kv_unified = true;in the same branch. Unified means one shared cache of-ccells that any single slot may consume in full, so the tables above are the whole KV bill and one conversation can still reach 98,304. Passing-np 4explicitly skips that branch, leaveskv_unifiedfalse, andsrc/llama-context.cppthen computescparams.n_ctx_seq = cparams.n_ctx / cparams.n_seq_max;— same total memory, a quarter of the context per conversation. If you want one slot, say-np 1, not-np 4. Also runtime, not backend.
The long-context alternative
./build/bin/llama-server \
-m ./nanbeige4.2-3b/Nanbeige_Nanbeige4.2-3B-Q8_0.gguf \
--host 127.0.0.1 --port 8080 \
-ngl 99 \
-c 196608 \
-fa on \
-ctk q4_0 -ctv q4_0 \
--temp 0.6 --top-p 0.95 --top-k 20
14.478 GiB accounted, 1.522 GiB left — the tightest configuration this page will recommend, and only on a headless card. Drop to -c 131072 for 11.135 GiB if the Radeon is also driving your monitors.
For agentic and tool-use work the model card's sampling table recommends --temp 1.0 with 65,536 new tokens; for reasoning and chat, 0.6 with 131,072.
preserve_thinking is a real decision on this card
The chat template takes preserve_thinking, which controls whether reasoning from earlier assistant turns stays in the context. A Nanbeige team member on discussion #9: "In our evaluations, enabling preserve_thinking generally provides better performance and better kv-cache reuse, so we recommend keeping it enabled when context length and memory allow." Note the conditional at the end. On a 24 GB card at 262,144 tokens it is free; here the window is 98,304 and retained reasoning consumes it, so leave it on by default and turn it off for long multi-turn agentic sessions that start truncating. Pass it through llama-server's OpenAI-compatible endpoint as "chat_template_kwargs": {"preserve_thinking": true}.
Environment variables: two to know, one not to set
HIP_VISIBLE_DEVICESselects which GPU the process sees — the documented way to pin a card in a multi-GPU box.GGML_CUDA_ENABLE_UNIFIED_MEMORY=1exists but is for integrated graphics.docs/build.mdis explicit: "However, this hurts performance for non-integrated GPUs". Leave it unset on a discrete Radeon.HSA_OVERRIDE_GFX_VERSION— do not set it, per the gfx-target section above.
Results
- Speed: omitted. There is no measurement of this model on any AMD GPU. I searched:
/check/nanbeige4-2-3b/rx-7800-xt(zero benchmarks); all 28 discussion threads on the canonical HuggingFace repo, fetched individually and grepped as one corpus — the stringsamd,rocm,radeon,gfx,7800and7900appear nowhere except once as the CPU ISA "AMD64" in a build banner; and llama.cpp's issue and PR search, wherenanbeigereturns 5 items (so the repo is indexed) andnanbeigecrossed withgfx1101returns none. The only throughput figures that exist for this model anywhere are a community report on an 8 GB NVIDIA card, posted bythermi6on discussion #18 — different vendor, different capacity — and interpolating from them would be inventing a number. If you run this, please contribute your measurement so the next reader gets a real one. - VRAM usage: 13.588 GiB accounted at the lead configuration (4.130 GiB weights + 8.766 GiB cache + 0.317 GiB reserved logits + 0.375 GiB FlashAttention dequant scratch), leaving 2.412 GiB of the card's 16 GiB; 14.478 GiB at the 196,608-token alternative. Both derived, not measured — see /check/nanbeige4-2-3b/rx-7800-xt. Separately, budget ~96 MiB of system RAM for the pinned attention mask at this context.
- Quality notes: I found no evaluation of this model under a quantized KV cache at any tier — not in the model card, not in the 28 canonical discussions, not in either GGUF repo's card, not in llama.cpp's tracker. Treat that as a gap, not as evidence the loss is small.
q8_0cache is generally treated as near-lossless across the llama.cpp ecosystem, which is why it is the lead here rather thanq4_0; the 4-bit rung is the aggressive one and it is what the long-context alternative spends to reach 196,608. The vendor publishes no throughput figures of its own at any hardware tier — the card carries quality benchmarks and a sampling table and nothing about speed. - Context is trained, not extrapolated:
rope_scalingisnullinconfig.json, so the model's 262,144 figure is a training target rather than a RoPE-scaling claim — which is why this page frames 98,304 as a card limit rather than a model one.
For the full benchmark data, see /check/nanbeige4-2-3b/rx-7800-xt.
Troubleshooting
llama_model_load: error loading model architecture: unknown model architecture: 'nanbeige'
Your build predates b10153. Rebuild from mainline master, or use a release tag at or above b10153 — that build's commit is the merge of PR #25994. On discussion #23 one user reports still being unable to load the architecture and a second reports that a later mainline release loads it. Distribution channels lag: the model card itself notes that LM Studio's bundled llama-server does not support nanbeige.
clang: error: cannot find ROCm device library
A ROCm install-layout problem, not a llama.cpp one, and docs/build.md documents the fix: find the directory under HIP_PATH containing oclc_abi_version_400.bc and prepend HIP_DEVICE_LIB_PATH=<that directory> to the cmake command.
VRAM climbs during a long session, or the process OOMs well below the budget above
This is the residual ROCm allocator problem, and on a 16 GB card there is less room to absorb it than on a 24 GB one. llama.cpp defines GGML_USE_VMM on HIP builds unless you pass -DGGML_HIP_NO_VMM=ON, and the startup banner prints VMM: yes or VMM: no per device — but a llama.cpp contributor states on issue #22107 that "virtual memory is completely broken in rocm since ROCM 7.0 with zero movement from AMD", tracking it at ROCm/rocm-systems#2516, which is still open. Without working VMM the runtime falls back to a pool that keeps buffers at their peak size, and that issue's report is of the pool growing until it faults.
Two things bound your exposure, and one of them is the reason this page can lead with a quantized cache at all. The FlashAttention dequant temporaries — historically the worst offender, because they scale with context — are not pool allocations at b10153 and later: ggml_cuda_flash_attn_ext_get_f16_extra_data takes them from the destination tensor's own allocation, and ggml_cuda_pool_alloc is used in that function's callers only for KV_max, dst_tmp and dst_tmp_meta. That is the 0.375 GiB already counted in the budget, not an unbounded leak. And -DGGML_HIP_NO_VMM=ON at build time is the supported switch if you want to take the pool out of the picture and compare. If you see growth, record it and file it: a real gfx1101 trace is worth more than this paragraph.
Out of memory at startup with the default cache type
You almost certainly left -ctk/-ctv at f16. At 98,304 tokens that alone is 16.500 GiB of cache — more than the whole card — and the default -c behaviour makes it worse rather than better. Either pass the cache-type flags as shown, or stay at -c 49152, where f16 fits at 12.697 GiB accounted and pays no dequant scratch at all.
Mismatched -ctk / -ctv silently disables FlashAttention
See the flag notes under Running. A stock HIP build compiles only the four matched vector instances; an asymmetric cache needs -DGGML_CUDA_FA_ALL_QUANTS=ON. The symptom is not an error message — the dispatcher just returns BEST_FATTN_KERNEL_NONE and you lose the kernel you were counting on.
Tool calls come back as plain text instead of executing
Fixed upstream in release tag b10227, and nothing about it was AMD-specific. The defect lived in llama.cpp's chat parser, above the backend: the model sometimes emits <tool_call> followed by a space rather than a newline, the parser matched the marker with the newline attached, and the call came back verbatim as content. The author of PR #26324 put the pre-fix rate at roughly 25%, and the same defect is reported independently on the model's own discussion #17, whose author measured 24 of 29 calls parsing before his patch and 29 of 29 after.
#26324 never merged — a maintainer objected that blanket whitespace trimming had been reverted once already for degrading other models' output — and the repair came through PR #26252, a specialized parser merged on 2026-08-02 and released as b10227; #26324's author closed his own pull request on 2026-08-10 with "Tried b10335 (#26252 was merged in b10227) and nanbeige tool calls work 100% of the times, thank you". Because the parser sits above the backend, this HIP build takes the fix from the same tag as any CUDA or Metal one: b10153 loads the architecture, b10227 parses tool calls. If calls still arrive as text, rebuild.
HTTP 400 — Failed to initialize samplers on a json_schema request
Any request with a response_format of json_schema fails at sampler init on the default jinja chat path, before a token is generated. Two users reproduced it independently on discussion #6, on different builds and quant tiers, and both found the same workaround: add --no-jinja to the llama-server command. Overriding the surface template with --chat-template chatml does not help, and the raw /completion endpoint with the same schema returns conformant JSON — which isolates the fault to the chat-completions grammar trigger rather than the model. The cost of --no-jinja is that you lose the model's own chat template, so apply the prompt format yourself.
ollama run nanbeige/nanbeige4.2:3b-Q4_K_M returns not found
The model card lists that command, but the registry entry it names does not exist, and neither do the library/ paths for either spelling of the name. Use llama-server directly.
A note on Vulkan and on Windows
llama.cpp also has a Vulkan backend that runs on this card, and Windows HIP builds exist. Everything above — the kernel tables, the matched-type restriction, the scratch arithmetic — was verified against the HIP backend on Linux and nothing here should be assumed to carry to either. docs/build.md notes separately that HSA_OVERRIDE_GFX_VERSION is not supported on Windows at all.
Anything else, or a real throughput measurement on this card, is welcome via the submission form.