self-hosted/ai
§01·recipe · llm

Apodex 1.1 mini on RTX 4080 SUPER: a 36B agent in 16 GB, and what the SUPER badge buys

llmadvanced16GB+ VRAMAug 31, 2026

This advanced recipe sets up Apodex 1.1 mini on the RTX 4080 Super, needing about 16 GB of VRAM.

models
tools
prerequisites
  • NVIDIA RTX 4080 SUPER (16 GB GDDR6X, 256-bit, Ada Lovelace) — the weights do NOT fit 16 GB alone; the routed experts of the first 17 blocks live in system RAM
  • 32 GB system RAM — 7.752 GiB of expert weights are resident in RAM at this recipe's setting, before llama-server's prompt cache
  • Dual-channel memory — between a third and two thirds of every decode step is spent reading experts out of your DIMMs, depending on their speed and how deep your context is
  • CUDA 11.8 or newer toolkit if you build from source — below it llama.cpp emits no sm_89 code and your card runs JIT-compiled PTX
  • llama.cpp build b10630 or newer — the build the lead quant was produced with
  • ~22 GB free disk for the Q4_K_M GGUF

What You'll Build

A local OpenAI-compatible endpoint serving Apodex-1.1-mini — an Apache-2.0 agent model that Apodex AI fine-tuned from Qwen/Qwen3.5-35B-A3B — on a single RTX 4080 SUPER, at a 131,072-token working context, from a Q4_K_M GGUF that is larger than the card.

That last part is the page. The Q4_K_M file loads 19.908 GiB of weights and the RTX 4080 SUPER holds 16 GiB, so nothing here is a fit calculation in the usual sense. It runs because the model is a sparse Mixture-of-Experts and llama.cpp will leave the routed expert tensors of the first N blocks in system RAM: 123 tensors carry 18.586 GiB of the file, and across the forty loaded blocks that is 18.164 GiB of the 19.908 GiB resident — 91.2%. Everything the card actually needs to keep — attention, the Gated-DeltaNet mixers, the shared experts, the router, the embeddings and the output head — is 1.744 GiB. The recipe is a choice of how much of that 91.2% to push across, and what the choice buys you in context.

Hardware data: RTX 4080 SUPER (16 GB GDDR6X, 256-bit, Ada Lovelace, CUDA capability 8.9) · 13.546 GiB derived working set at -ncmoe 17, 131,072-token context · See benchmark data

🧭 What is specific to this card, and what is not — stated before the arithmetic rather than after it.

Nothing about capacity is specific to this card. The offload ladder, the -ncmoe 17 choice, the VRAM budget and the quant decision below are properties of the file at a given setting on 16 GiB, and they are byte-for-byte what any 16 GB board gets — an RTX 4080, an RTX 4060 Ti 16GB, an RTX 5080. If you own one of those and landed here, every table in the capacity half of this page is yours as written. Pages that pretend otherwise are inventing a difference.

What is specific is where a decode step spends its time, and on this board the answer is unusually balanced. At -ncmoe 17 a generated token reads 0.260 GB from system RAM and 2.00–3.43 GB from VRAM depending on how full your context is. Those two halves cost the same wall-clock only when the card's bandwidth is 7.70× to 13.18× your DIMMs'. This board reads its own memory at 736 GB/s, so against the most ordinary pairing it meets — dual-channel DDR5-6000, 96 GB/s — the ratio is 7.667× against a 7.696× threshold on an empty context. The two halves of a decode step are 50.1% / 49.9% on a fresh conversation, and the card's share grows to 63% by the time the window is full. Neither half dominates; both are worth optimising. Where the decode time actually goes derives it, and derives it for slower and faster memory, where the answer changes.

And the SUPER badge is measurable rather than rhetorical here. Our own catalogue holds the same benchmark on this board and on the plain RTX 4080, and across three models the SUPER's decode advantage is +1.5%, +3.6% and +2.0% — a mean of +2.34%, computed from the t/s rather than from those rounded percentages — against a memory-bandwidth advantage of +2.68% and a CUDA-core advantage of +5.3%. It tracks the memory, not the shaders. What the SUPER badge buys shows the numbers. On this recipe you should expect less than that again, because most of a decode step is spent in memory the badge does not touch.

⚠️ This model does not fit 16 GB on its own, and system RAM is a prerequisite rather than a footnote. At the setting below, 7.752 GiB of expert weights are resident in host RAM for as long as the server runs, and llama-server's prompt cache can commit several GiB more on top (see System RAM). bartowski's own sizing rule for this file is to "add both your system RAM and your GPU's VRAM together" and pick a quant a couple of GB below the sum (model card); with 20.363 GiB of weights that is satisfied on paper by 16 GB of RAM, and in practice you want 32.

ℹ️ This recipe is text-only, deliberately. The checkpoint contains a vision tower and the GGUF repos ship a projector for it, but the vendor documents the model as pipeline_tag: text-generation and its model card carries no image-input instructions at all — the words "vision", "visual", "multimodal" and "mmproj" appear zero times in it. Every number below is for text generation, and the run command refuses the projector explicitly rather than merely omitting --mmproj. Our RTX 3090 page sets out the same reasoning at length.

⚠️ Two version floors, about 2,200 builds apart — and a third reason to be current that is about speed rather than support.

  • The architecture. LLM_ARCH_QWEN35MOE is absent from src/llama-arch.cpp at release b7989 and present at b7990, so it arrived in (b7989, b7990].
  • The qwen35moe-specific MTP wiring — the load_mtp / TENSOR_SKIP handling in src/models/qwen35moe.cpp that keeps block 40 off the card until you ask for it — absent at b10211, present at b10212, so it arrived in (b10211, b10212].
  • A throughput floor, not only a support floor. llama.cpp #25162 — closed as completed on 2026-08-03 — measured a 24–42% regression on Turing (SM75) in ssm-scan.cu / ssm-conv.cu, the kernels behind 30 of this model's 40 layers, while noting "Gemma4 (no SSM layers) is unaffected". What is established is that performance had recovered by the tree the reporter retested at (0b14b87d7, 2026-08-03) and that the cause was never identified in the thread — the only fix candidate concerned FlashAttention rather than the SSM kernels, and the reporter records that restoring parameter-level __restrict__ did not restore performance. Whether the same codegen effect ever reached compute capability 8.9 is unmeasured, and this page does not claim it did. It is a reason to be current, not a reason to believe your card was affected.

Both intervals are written as intervals on purpose: each is bracketed by adjacent published tags, which is as fine as a tag-granularity probe can resolve. Use b10630 or newer — the build the lead quant was produced with. That is a sufficient floor, not a bisected minimum. Every source reference on this page was read at release b10666 (4e97ac86ebe2c4cb8212d98d2641ad6768810896, confirmed by dereferencing the tag), and every line number below is that tree's. The tag moves several times a day — b10715 was current when this was written, 2026-08-31 — so re-read any offset you care about at whatever you actually build.

Requirements

ComponentMinimumThis recipe
GPU16 GB VRAM (NVIDIA, CUDA)RTX 4080 SUPER (16 GB) — not measured; the budget below is derived from the artifact and the runtime source (/contribute)
RAM16 GB with the prompt cache capped32 GB, dual-channel — 7.752 GiB of experts are resident at -ncmoe 17, plus the prompt cache
Storage21.86 GB for the Q4_K_M GGUF (per the HF tree API)21,864,082,336 bytes, re-read 2026-08-31
SoftwareCUDA 11.8+ toolkit, llama.cpp ≥ b10630b10666

What NVIDIA publishes about this board, and what it does not

From NVIDIA's RTX 4080 family spec table, read 2026-08-31, which lists the SUPER and the plain 4080 side by side:

NVIDIA's published specRTX 4080 SUPERRTX 4080
NVIDIA CUDA Cores102409728
Shader TFLOPS5249
Tensor (AI TOPS)836780
Boost / base clock (GHz)2.55 / 2.292.51 / 2.21
Standard Memory Config16 GB GDDR6X16 GB GDDR6X
Memory Interface Width256-bit256-bit
NVIDIA ArchitectureAda LovelaceAda Lovelace
PCI Express Gen 4YesYes
CUDA Capability8.98.9

Two absences on that page matter, and both were counted today rather than assumed. NVIDIA publishes no memory-bandwidth figure and no memory-speed figure — the strings Bandwidth, GB/s, GB/sec, Memory Speed and Gbps occur zero times on it, counted case-sensitively over the rendered text. (Lower-cased, one bandwidth survives — in a display-configuration footnote about how many monitors the outputs can drive. It is not a memory figure, and there is no other.) And NVIDIA publishes no PCIe lane countlanes and x16 occur zero times; the generation is a vendor fact, the width is not.

So the 736 GB/s this page uses is a derivation, and it is stated as one. A board partner's sheet gives the memory speed: ASUS's TUF-RTX4080S-16G-GAMING techspec lists "Memory Speed 23 Gbps" alongside "Memory Interface 256-bit" and "Bus Standard PCI Express 4.0". GDDR6X uses PAM4 signalling — two bits per cycle — so the familiar data rate × bus bytes arithmetic describes it exactly: 23 Gbps across 32 bytes is 736 GB/s. That the arithmetic is the right one is checkable against NVIDIA's own numbers: the RTX Blackwell architecture whitepaper gives the RTX 4080 716.8 GB/sec at 22.4 Gbps on the same 256-bit interface, and the RTX 5080 960 GB/sec at 30 Gbps, both of which are exactly data rate × 32. Hardware Corner's page for this card publishes the same 736 GB/s directly. As published on 2026-08-31, our own Qwen3.8-27B page for this board derives it the same way, from the same ASUS figure.

Check yours without trusting any of that. GDDR6X is double-data-rate, the bus is 32 bytes wide, and nvidia-smi reports the memory clock:

nvidia-smi --query-gpu=name,memory.total,clocks.max.memory,pcie.link.gen.max,pcie.link.width.max --format=csv

clocks.max.memory × 2 × 32 B is your board's peak bandwidth; the last two columns are the only reader-verifiable statement of your link, which is a property of your slot as much as your card. This page asserts no lane count.

For your DIMMs, do the arithmetic from their rated transfer rate: dual-channel DDR5-6000 is 6000 MT/s × 8 B × 2 = 96 GB/s, dual-channel DDR4-3200 is 51.2 GB/s, and a single-channel configuration halves whichever you have. On this page that number is as important as the card's.

Installation

1. Get a llama.cpp build that knows the architecture

The architecture string baked into the GGUF is qwen35moe, registered in src/llama-arch.cpp as LLM_ARCH_QWEN35MOE. Download a release binary at b10630 or newer, or build from source:

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
git checkout b10666
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j

⚠️ If you build in a container, cross-compile, or pass -DGGML_NATIVE=OFF, check your CUDA toolkit version first. At b10666 ggml/src/ggml-cuda/CMakeLists.txt appends 75-virtual 80-virtual 86-real unconditionally at :34, and gates 89-real 90-virtual behind CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.8" at :36-37. There is no 86-virtual in that list. So on a toolkit older than 11.8 your sm_89 card gets no native cubin and no near-neighbour PTX — it JIT-compiles from 80-virtual, silently, with no warning anywhere. GGML_NATIVE defaults ON and the native branch at :27 covers most people building on the machine they will run on; it is the other builds that are exposed. This is an Ada-specific trap: an Ampere card has 86-real unconditionally.

2. Download the Q4_K_M GGUF

pip install -U huggingface_hub
hf download bartowski/apodex_Apodex-1.1-mini-GGUF \
    apodex_Apodex-1.1-mini-Q4_K_M.gguf --local-dir ./apodex-1.1-mini

That is bartowski/apodex_Apodex-1.1-mini-GGUF apodex_Apodex-1.1-mini-Q4_K_M.gguf, 21,864,082,336 bytes (20.363 GiB), imatrix-calibrated and produced on llama.cpp b10630. Byte count and file list re-fetched from the HF API on 2026-08-31; the repo's lastModified is 2026-08-26T09:14:35Z.

Q4_K_M on a 16 GB card is a deliberate choice, and the obvious alternative is narrower than it looks. Ten of bartowski's rungs are under 16 GiB on disk, so "small enough to load" is not the constraint. The constraint is what has to sit beside the weights. Hold this page's own terms — 32,768 tokens of q8_0 KV (0.332 GiB) and the recurrent state (0.061 GiB), against the ceiling derived below — and a fully resident configuration needs weights at or below 13.524 GiB — this page's 13.917 GiB ceiling minus those two terms. That is Q2_K_L (12.650) and everything smaller; the whole 3-bit tier misses it, IQ3_XXS at 14.287 GiB and Q3_K_S at 14.886. So the escape from offload is not "drop to 3-bit", it is "drop to 2-bit" — and that is exactly where the publisher of a second GGUF set draws the line: abenzerps suggests IQ2_M or IQ3_M as a 16 GB starting point and then closes the section with "Prefer Q4_K_M or higher when system memory permits." This recipe follows that advice, because the model is an agent that runs tool calls and multi-step plans, and 2-bit weights are the wrong place to save on a workload where a single malformed argument ends the run. A 3-bit rung with a shallow -ncmoe is a reasonable middle and this page does not measure it either. Capacity-driven — identical on any 16 GB card.

Running

./build/bin/llama-server \
    -m ./apodex-1.1-mini/apodex_Apodex-1.1-mini-Q4_K_M.gguf \
    --no-mmproj \
    -ngl 99 \
    -ncmoe 17 \
    -np 1 \
    -c 131072 \
    -ctk q8_0 -ctv q8_0 \
    --load-mode none \
    --cache-ram 2048 \
    --temp 1.0 --top-p 0.95 --repeat-penalty 1.05 \
    --host 127.0.0.1 --port 8080

Then talk to it:

curl http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "apodex-1.1-mini",
    "messages": [{"role": "user", "content": "Plan a three-step verification pass for a CSV of quarterly revenue."}]
  }'

Sampling values are the vendor's own recommendation from the model card, whose Best Practices block reads temperature: 1.0, top_p: 0.95, repetition_penalty: 1.05. Two of those three are confirmed by the artifact itself and the third is not, which is worth knowing before you assume the GGUF's own defaults match the card. Of the header's 52 keys, general.sampling.temp is 1.0 and general.sampling.top_p is 0.949999988079071 — but there is no repetition-penalty key of any spelling, so --repeat-penalty 1.05 above comes from the card alone. The header does carry a fourth value the card never mentions: general.sampling.top_k = 20, where the string top_k occurs zero times in the model card. This command passes the card's three and leaves top_k alone. llama-server uses the chat template embedded in the file by default.

Eight of those flags are decisions rather than defaults, and each default below was read at b10666 for this page. Each is labelled capacity-driven — meaning it is the same on any 16 GB card — or bandwidth-driven, meaning it is not.

  • -ngl 99 puts every layer on the GPU as the starting point. -ncmoe then walks part of it back off. Capacity-driven.
  • -ncmoe 17 is this recipe's subject. Its help text at this pin is "keep the Mixture of Experts (MoE) weights of the first N layers in the CPU" (common/arg.cpp:2789-2790), and Choosing your own -ncmoe derives 17 as the shallowest rung that clears this page's ceiling at 131,072 tokens. It is not a measured optimum and it is not inherited. Capacity-driven — and on this card shallow is also the fast direction, so the two arguments agree rather than trade off.
  • --no-mmprojllama-server honours the projector auto-download, and both no_mmproj (false, common/common.h:596) and mmproj_use_gpu (true, :594) default that way, so a bare -hf …-GGUF fetches a sibling projector and puts it on the GPU. The lead repo ships two, at 899,283,488 and 902,822,432 bytes (0.838 and 0.841 GiB), and the resolver picks one of them — so the cost is 0.838 GiB, just under the 0.844 GiB that the two cheapest steps of -ncmoe would free, spent on a capability the recipe does not use. Capacity-driven.
  • -np 1 — with no slot count, common/arg.cpp:1400 sets params.n_parallel = -1; // auto by default for the server example, and tools/server/server.cpp resolves that to four slots. The Gated-DeltaNet recurrent state is per sequence, so four slots quadruple it — 0.245 GiB instead of 0.061. One slot is right for a single-user agent loop. Capacity-driven.
  • -c 131072 is half the model's declared context_length of 262144. The ladder below prices the other half; it costs three more steps of -ncmoe. Both, and on this card it is the single largest speed lever — capacity-driven for the 1.328 GiB the cache occupies, and bandwidth-driven because that cache is re-read from VRAM on every generated token. Context depth moves the crossover from 7.70× on an empty window to 13.18× on a full one, and on this board that swing takes the card's share of a decode step from 50% to 63%.
  • -ctk q8_0 -ctv q8_0 quantises the KV cache. block_q8_0 is a 2-byte scale plus 32 one-byte quants — 34 bytes per 32 elements, i.e. 8.5 bits per element, not 8. It takes the 131,072-token cache from 2.500 GiB (20,480 B/token at f16) to 1.328, which on this card is a saving of 1.172 GiB — worth between two and three steps of -ncmoe you do not have to spend, since a step costs 0.4219 or 0.4863 GiB so two come to 0.844–0.973 and three to 1.266–1.459. Both — capacity-driven for the 1.328 GiB, and bandwidth-driven because halving the cache halves the largest single term in the decode budget, worth about 1.94 ms per token at a full window. It also decides a flag you are not passing: a quantised V cache requires FlashAttention, so with the default -fa auto src/llama-context.cpp turns it on, and an explicit -fa off alongside -ctv q8_0 is a hard error — the context is never created. See If you have to turn FlashAttention off for what that branch actually costs, because it is not the KV delta people assume.
  • --load-mode none — llama.cpp asks for this itself. When a CPU tensor override meets memory mapping, src/llama-model-loader.cpp prints "tensor overrides to CPU are used with mmap enabled - consider using --load-mode none for better performance". An overridden tensor is given the first workable buffer type from a list src/llama-model.cpp builds in the order "ACCEL -> GPU host -> CPU extra -> CPU", whose GPU-host entry is pinned memory the card can DMA from; the loader downgrades that to ordinary CPU memory whenever mmap is on. Bandwidth-driven, on the host side — and this is the half of the decode step your DIMMs own. One reporter tuning this same offload path on a 16 GB card measured a short generation going "from about 27.6 t/s with mmap to about 31.4 t/s with --load-mode none, with a much larger improvement in prompt processing" (llama.cpp #25859 — a different card, model and backend, so read it as a direction rather than a number).
  • --cache-ram 2048 — see System RAM. The default is 8192 MiB (common/common.h:632, flag at common/arg.cpp:1713) and on this architecture that is not a small number. Neither — it is a host-memory decision.

What -ncmoe actually moves

-ncmoe N / --n-cpu-moe N is arch-agnostic. It appends one buffer-type override per block index i in 0 … N-1, each built from LLM_FFN_EXPS_REGEX at common/common.h:1130:

const char * const LLM_FFN_EXPS_REGEX = "\\.ffn_(up|down|gate|gate_up)_(ch|)exps";

It matches tensor names, not architectures. In this GGUF exactly 123 tensors match — ffn_down_exps, ffn_gate_exps and ffn_up_exps, three per block across all 41 blocks. The override is applied at tensor-creation time, in the loader, before any buffer exists: a matching tensor is given a CPU-side buffer type and is then allocated and read into it. Those bytes never enter VRAM at all, which is why the flag behaves identically on a hybrid recurrent model and on a plain transformer MoE.

Three things it does not move, all of which stay on the card:

  • The Gated-DeltaNet mixers and the attention weights. A GDN block's ssm_* tensors, its attn_qkv and attn_gate, and a full-attention block's attn_q/k/v/output are untouched by the regex.
  • The shared expert and the router. ffn_down_shexp, ffn_gate_shexp, ffn_up_shexp, ffn_gate_inp and ffn_gate_inp_shexp do not match _exps, so the dense path every token takes stays resident.
  • The KV cache and the recurrent state. Both pick their buffer type from the layer's device, decided by -ngl alone. So a block whose experts are in RAM still keeps its recurrent state in VRAM — which is what makes the budget below decomposable at all.

Add it up and -ncmoe 40 leaves 1,872,620,032 bytes (1.744 GiB) on the card.

Whether llama.cpp really honours this on a hybrid recurrent architecture is a fair question and the answer is not from us. It is from a reporter who profiled the path with Nsight and describes the transfers directly — "the GPU sits idle a large fraction of each prefill pass waiting on expert-weight H2D copies" (#25859, open, 8 comments, reopened) — which is only possible if the weights are on the host to begin with; and from the same thread's 16 GB tuning report, whose -ncmoe sweep changes throughput by 16% between adjacent rungs, which is not the behaviour of an ignored flag. Both are on Qwen3.6-35B-A3B, the sibling architecture, not on Apodex. Nobody has published an -ncmoe run of this file.

The VRAM budget

Every figure here is derived from the artifact's own tensor table and from llama.cpp's allocation code at b10666. Nothing on this page was measured on an RTX 4080 SUPER.

ComponentBytesGiB
Weights on GPU (Q4_K_M, MTP block skipped, experts of blocks 0–16 in RAM)13,052,537,34412.156
KV cache, 131,072 tokens at q8_01,426,063,3601.328
Recurrent state, 30 Gated-DeltaNet layers × 1 sequence65,863,6800.061
Total14,544,464,38413.546

Each term, so you can check it rather than take it:

  • Weights. The file's tensor payloads sum to 21,853,090,304 bytes, of which 476,956,672 are blk.40, the multi-token-prediction block that TENSOR_SKIP keeps off the card by default — leaving 21,376,133,632 (19.908 GiB) resident. -ncmoe 17 moves 8,323,596,288 of that (7.752 GiB) to the host. ⚠️ The file on disk is 21,864,082,336 bytes and the resident set is 21,376,133,632; those are different numbers and the difference is not blk.40 alone. It is 487,948,704, of which blk.40 is 476,956,672 and the remaining 10,992,032 B (10.5 MiB) is the file's non-tensor content — GGUF header, key-value metadata, the tensor-info table and inter-tensor alignment padding. That residual is exactly 21,864,082,336 − 21,853,090,304, both of which are printed above. Free ~22 GB of disk; budget 19.908 GiB of memory.
  • KV cache. full_attention_interval is 4, so full attention sits on blocks where i % 4 == 3[3, 7, 11, 15, 19, 23, 27, 31, 35, 39], ten of forty, verified against the tensor list. Ten layers × head_count_kv 2 × key_length 256 × 2 (K and V) × 8.5 bits = 10,880 B/token. The other thirty layers carry Gated-DeltaNet recurrent state instead and contribute nothing that grows with context.
  • Recurrent state. 2,195,456 bytes per layer per sequence, 65,863,680 across thirty. llama-server prints it at startup as RS buffer size.

Against the card's nominal 16 GiB that leaves 2.454 GiB — but a 16 GB card does not give you 16 GiB, and this page's ceiling is tighter than the nominal one:

1. The driver and the CUDA context take a share the model budget never sees. The one expert-offload run in our catalogue that writes the numbers down is gemma4-26b on an RTX 3060 at -ncmoe 12, whose submitter recorded a peak of 11,179 MiB of that card's 12,288 on a headless box (benchmark id 280, re-read 2026-08-31) — so at most 1,109 MiB was unavailable to the model there. ⚠️ That is a different board in a different VRAM tier, and the term is a context plus a driver reservation rather than a percentage, so borrowing the absolute 1,109 MiB onto a 16 GB card transfers a quantity rather than measuring this one. It is the best number in the catalogue and it errs in the safe direction: this page reserves 1,109 MiB, and a Windows box driving a monitor should expect to want more.

2. Compute buffers are excluded from every table on this page, and they are real. This page reserves a further 1,024 MiB for them, which is llama.cpp's own default per-device fit margin (common/common.h:481, 1024 * 1024*1024) — a defensible constant rather than a measurement. The nearest thing to a measurement is llama.cpp #27698 (open, 0 comments), where a run with expert offload honoured then failed context creation on a ~504 MiB compute buffer with cudaMalloc failed: out of memory. ⚠️ That branch of that issue is gemma4moe (gemma-4-26B-A4B), not qwen35moe — the issue's qwen35moe branch fails differently and earlier — and the hardware is an RX 6700 XT. So it establishes that the wall exists on an MoE under offload; it does not size it for this architecture or this card.

That gives this page's working ceiling: 16,384 − 1,109 − 1,024 = 14,251 MiB = 13.917 GiB. The configuration above lands at 13.546, clearing it by 0.371 GiB.

Read the real numbers off the server's own startup log rather than trusting any of this: llama.cpp prints model buffer size once per buffer, so with -ncmoe you get a CUDA line and a host line and the split above is directly checkable, followed by KV buffer size, RS buffer size and compute buffer size. Check the sum against nvidia-smi --query-gpu=memory.total,memory.used --format=csv. If you do, please send the numbers; this pair has no benchmark in our catalogue.

The offload ladder

The ladder is not linear, and this is the part that does not transfer from any other recipe. bartowski upcasts ffn_down_exps to Q6_K in 20 of the 40 loaded blocks — 0, 1, 2, 3, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, and then 34 through 39 — so one step of -ncmoe costs either 0.4219 GiB (Q4_K/Q4_K/Q4_K) or 0.4863 GiB (Q6_K/Q4_K/Q4_K) depending on which block it lands on. Blocks 0–4 and 34–39 are all the expensive kind. Any sentence of the form "a step of -ncmoe costs X GB" is wrong on this file; read the cumulative column instead.

-ncmoeexperts moved to RAMweights left on GPU
00.000 GiB19.908 GiB
83.76216.146
125.51414.394
146.42213.486
167.26612.642
177.75212.156
209.08210.826
2410.8349.074
3013.4946.414
4018.1641.744

Weights only — no KV cache, no recurrent state, no compute buffers. The left column is a system-RAM requirement, and it is the number to size your host against. Capacity-driven throughout: this table is the same on every 16 GB card, and on every 24 GB one too — what changes is which rung you need.

Choosing your own -ncmoe

Against the 13.917 GiB ceiling above, at 131,072 tokens with q8_0 KV and one sequence slot:

-ncmoeGPU totalMargin against 13.917 GiB
1614.032 GiB−0.115 — does not fit
1713.546 GiB+0.371
1813.124 GiB+0.793

So 17 is the shallowest rung that clears, and it clears by a third of a gigabyte rather than by a comfortable margin. Shallower is better for speed on this card — every block you leave on the GPU is 0.42–0.49 GiB of expert weight that stops being read across your DIMMs — so the right answer is the smallest number that fits, not a round one.

The whole recipe then collapses to one trade, because the KV cache is linear in context and the price of a doubling is the whole cache you already had:

Context-ncmoeGPU totalMarginExperts in system RAM
32,7681413.880 GiB+0.0376.422 GiB
131,0721713.546 GiB+0.3717.752 GiB
262,1442013.544 GiB+0.3739.082 GiB

All three rows use -ctk q8_0 -ctv q8_0 and -np 1. All three are arithmetic. Note how thin the 32,768 row is: -ncmoe 14 clears by 0.037 GiB, which is less than a tenth of a single step, so if anything on your machine wants a little more than this page's reserve that row is the first to fail. -ncmoe 15 at 32,768 costs you nothing you will notice and buys back 0.42 GiB.

The middle row is what the command above runs, and it is chosen so that this page and the RTX 3090 page document the same context — the entire difference between a 24 GB card and this one is that 7.752 GiB of experts have moved off the card and into RAM.

Where the work happens: prompt processing versus generation

The single most useful thing to understand about this configuration is that the offloaded experts are not treated the same way in the two phases, and llama.cpp's scheduler says so in one rule.

An operation whose weights live in a host buffer is normally run on the CPU — ggml/src/ggml-backend.cpp puts it plainly: "operations with weights are preferably run on the same backend as the weights". But immediately after that the scheduler will "check if a backend with higher prio wants to offload the op", and asks the GPU. The CUDA backend answers with a single test in ggml/src/ggml-cuda/ggml-cuda.cu: the op's batch size must be at least 32, a threshold whose only override is the GGML_OP_OFFLOAD_MIN_BATCH environment variable.

So the same weights take two completely different paths:

  • Prompt processing runs a batch of hundreds of tokens (-ub defaults to 512, common/common.h:452), so the expert matmuls are offloaded to the GPU and the expert weights are copied host → device over PCIe, layer by layer, for every physical batch. This phase is bus-bound. The reporter above profiled exactly this and found that "prefill is bottlenecked by PCIe transfers, not GPU compute", with the copies running "serially with the matmul that consumes them" (#25859). Carry his caveats: a different model, one box, gen4-specific, and the profiling is older than the tree this page pins.
  • Token generation runs one token at a time. One is less than 32, so the offloaded expert matmuls stay on the CPU backend and those expert weights never cross the bus at all. This phase is bound by your system RAM bandwidth and your CPU, and the only thing moving over PCIe is a small activation vector per layer.

So a narrow PCIe link is a time-to-first-token problem, not a tokens-per-second problem. That is worth stating flatly because the opposite is widely believed, and it is the reason this page spends its effort on your DIMMs rather than on your slot.

Where the decode time actually goes

expert_used_count is 8 of 256, so one thirty-second of the expert bytes move per generated token. That splits a decode step into two halves that run on two different memories:

Per generated token, at -ncmoe 17Bytes
Activated experts of blocks 0–16, from system RAM8,323,596,288 ÷ 32 = 260,112,384
Activated experts of blocks 17–39, from VRAM11,179,917,312 ÷ 32 = 349,372,416
Non-expert resident weights, from VRAM1,586,555,392
Recurrent state, 30 GDN layers, from VRAM65,863,680
KV cache at occupancy T, from VRAM10,880 × T

The non-expert term is the 1,872,620,032 bytes of -ncmoe 40 minus token_embd.weight (286,064,640), because an embedding table is indexed, not read: one row per token. Everything else — output.weight, the attention projections, the GDN mixers, the shared experts, the router, the norms — is read in full on every step.

The KV cache is decode traffic, not just decode capacity. Attention re-reads the whole occupied cache from VRAM on every generated token, so at a full 131,072-token window that is 1,426,063,360 bytes — larger than every weight the card holds put together, and 1.94 ms of a decode step on this board. It is easy to price the cache as a capacity line and then leave it out of the bandwidth arithmetic; the budget table above treats it as capacity, and doing that here would move the answer rather than round it.

The two halves take equal wall-clock exactly when your card's bandwidth is VRAM bytes ÷ host bytes times your DIMMs'. That ratio needs no bandwidth figure at all, which is why it is the form to state first. Every ratio below contains the recurrent state and the KV cache at the stated occupancy, and excludes token_embd on both sides — several ratios differing only in which terms they hold are in circulation for this model, so naming them is not a formality.

Context occupancyVRAM bytes/tokenHost bytes/tokenCrossover ratioEqual-time DIMM bandwidth against 736 GB/s
empty2,001,791,488260,112,3847.696×95.64 GB/s
32,7682,358,307,328260,112,3849.066×81.18 GB/s
65,5362,714,823,168260,112,38410.437×70.52 GB/s
131,072 (this recipe's window)3,427,854,848260,112,38413.178×55.85 GB/s

And here is what makes this board different from the rest of its tier. The right-hand column is the system-memory bandwidth at which the two halves cost the same. Dual-channel DDR5-6000 delivers 96 GB/s. The top row asks for 95.64. They agree to within 0.4%. Put another way, on the most ordinary configuration this card ships in, a decode step on an empty context is 50.1% card and 49.9% DIMMs — and it is the only division this page found that is not lopsided.

Your system memoryCard ÷ RAM at 736 GB/sDecode is host-bound until
Single-channel DDR4-3200 (25.6 GB/s)28.750×far past 262,144 — host-bound at every depth
Dual-channel DDR4-3200 (51.2 GB/s)14.375×~160,000 tokens — host-bound throughout this recipe's window
Dual-channel DDR5-4800 (76.8 GB/s)9.583×~45,000 tokens
Dual-channel DDR5-5600 (89.6 GB/s)8.214×~12,000 tokens
Dual-channel DDR5-6000 (96 GB/s)7.667×~0 — the two halves are level on an empty context
Dual-channel DDR5-8000 (128 GB/s)5.750×already VRAM-bound before the first token

So the answer for this card is genuinely "it depends what you put next to it", and the boundary runs through the middle of the DDR5 range rather than sitting outside it. The crossover ratios are a property of this recipe's bytes-per-token split and do transfer to any 16 GB card at -ncmoe 17; everything to the right of them — the equal-time column, the per-DIMM table and the roofline below — is bandwidth-driven and stops at this board.

Priced out as a roofline — the two halves treated as serial, which they broadly are, since within a layer the CPU's expert matmul consumes what the GPU's router produced:

At 131,072 tokens of contextVRAM half (736 GB/s)Host halfTotalCeilingShare on the card
Single-channel DDR4-3200 (25.6 GB/s)4.66 ms10.16 ms14.82 ms~67 tok/s31%
Dual-channel DDR4-3200 (51.2 GB/s)4.665.089.74~103 tok/s48%
Dual-channel DDR5-5600 (89.6 GB/s)4.662.907.56~132 tok/s62%
Dual-channel DDR5-6000 (96 GB/s)4.662.717.37~136 tok/s63%
Dual-channel DDR5-8000 (128 GB/s)4.662.036.69~149 tok/s70%

On an empty context the same arithmetic gives 78 / 128 / 178 / 184 / 210 tok/s down the same rows, with the card's share falling to 21–57%.

⚠️ Those are ceilings from two bandwidth terms, not predictions. They exclude the CPU time to actually multiply the experts, kernel launch overhead, attention arithmetic, sampling and everything else, and they credit both memories with perfect efficiency. The nearest real measurement of this shape says the gap is large. The 16 GB owner tuning Qwen3.6-35B-A3B Q4_K_M at -ncmoe 20 on 32 GB of DDR5-5600 reports 36.75 t/s on a 121,269-token prompt, and 42.18 t/s at 48.5k (#25859) — figures in the tens, where the rows above are in the low hundreds. That is an RX 9070 XT under Vulkan on Windows with q4_0 KV, so it is not this card, this backend, this KV type or this model; it is a magnitude check, and the magnitude it gives is that you should expect a fraction of the bound rather than the bound. Read every row above as a number you cannot beat.

What the SUPER badge buys, measured rather than argued

Everything above is derivation. This section is the one place on the page with measurements on this exact board, and they are not ours: they come from Hardware Corner's LLM benchmark suite, which runs the same models at the same quantisations across many cards, and which our catalogue already stores for four cards of this capacity.

First, the SUPER against the plain 4080, same suite, same models, same context. NVIDIA's own table gives the SUPER +5.3% CUDA cores, +6.1% shader TFLOPS and +7.2% AI TOPS, against a memory advantage of +2.68% (736 vs 716.8 GB/s):

Same suite, 4k contextRTX 4080 SUPERRTX 4080SUPER's advantage
Qwen3 8B (Q4_K) — token generation104.19 t/s102.69 t/s+1.5%
Qwen3 14B (Q4_K) — token generation64.20 t/s61.97 t/s+3.6%
gpt-oss 20B (MXFP4) — token generation139.12 t/s136.45 t/s+2.0%
Qwen3 8B (Q4_K) — prompt processing6,136.98 t/s6,177.90 t/s−0.7%
Qwen3 14B (Q4_K) — prompt processing3,744.96 t/s3,574.72 t/s+4.8%
gpt-oss 20B (MXFP4) — prompt processing6,363.99 t/s6,218.50 t/s+2.3%

Decode tracks the memory advantage and not the compute advantage — +1.5 to +3.6% against a bandwidth gap of +2.68% — and prompt processing is noisy enough to come out negative on one of three models despite five per cent more shaders. That is the honest size of the badge on a language model: a couple of per cent, on the axis NVIDIA's spec table does not print.

Second, decode across the whole 16 GB tier, from the same publisher. Our catalogue stores one row per card, and they line up against bandwidth almost perfectly:

CardBandwidthQwen3 14B (Q4_K) decode @4kImplied bytes/token
RTX 4060 Ti 16GB288 GB/s27.4 t/s10.51 GB
RTX 4080716.8 GB/s62.0 t/s11.57 GB
RTX 4080 SUPER736 GB/s64.2 t/s11.46 GB
RTX 5080960 GB/s80.6 t/s11.91 GB

This table quotes our catalogue's stored values, which are rounded to one decimal; the badge table above quotes Hardware Corner's own precision, which is why the RTX 4080 appears as 62.0 here and 61.97 there. Same row, same run.

A 3.33× spread in bandwidth at identical capacity produces a 2.94× spread in decode, and the implied bytes-per-token is constant to within ±6%. Decode on a fully resident model is a bandwidth problem, and on this tier it is almost purely one. That is the assumption the roofline above rests on, and here it is holding on real hardware rather than being asserted.

Third — and this is the reason -c is labelled a speed lever above — the same board's own decode-versus-context curve. The gpt-oss 20B row runs deeper than the others:

gpt-oss 20B (MXFP4) on this board4k16k32k64k128k
Token generation139.12 t/s122.96107.1981.4560.01 t/s

A 2.32× decay from 4k to 128k, on this card, with the weights unchanged. The only thing that grew is the KV cache. That is the clearest available evidence that the cache is decode traffic rather than a static allocation, and it is measured on the board this page is about.

⚠️ Four caveats, because none of this is a number for Apodex. Every row is a different model, fully resident on the card, with no expert offload and therefore no host half at all — so it calibrates the VRAM side of the roofline and says nothing about the DIMM side. It is one publisher's suite with a published methodology but no per-run instrumentation. Our catalogue's copies (peak_vram_gb: 16.0 on all of them) record the board's capacity in the peak field, which is a data defect on our side rather than a measurement — do not read those as peaks. And the figures were last verified in our catalogue on 2026-05-15. There is still no measured throughput number for Apodex 1.1 mini on this card at any setting.

System RAM, and the prompt cache that will surprise you

At -ncmoe 17 the expert weights hold 7.752 GiB of host RAM for the life of the process. That is the part the ladder tells you about. There is a second consumer that it does not.

llama-server keeps a prompt cache, capped by --cache-ram at a default of 8192 MiB (common/common.h:632; the flag is common/arg.cpp:1713). For an attention-only model a cache entry is a few MiB and the default is invisible. For a hybrid recurrent model it is not. llama.cpp #27894 is the measurement: a reporter took a per-request RSS growth on another qwen35moe model for a memory leak — 40 consecutive requests, 1340 → 6379 MiB, perfectly linear — and then retracted it after a contributor pointed at the flag, writing "Confirmed — you're right, this isn't a leak." The decisive run was --cache-ram 512 with nothing else changed: growth continued at +126 MiB per request until it hit the bound and then stayed flat.

Two things to carry from it and one not to. Carry: the growth is the prompt cache, it is bounded by the flag, and the flag is the fix. It is not vendor-specific — the same reporter rebuilt with -DGGML_VULKAN=ON and measured +126.0 MiB/request against ROCm's +126.3. Do not carry: the reporter's identification of an entry as one full recurrent state. This model's recurrent state is 65,863,680 B = 62.8 MiB, and 126 is about twice that; the 126 stands as a measurement, its composition does not, and nobody has instrumented the difference. The issue is closed as completed, on an Ornith build rather than an Apodex one.

Hence --cache-ram 2048 in the command above: an explicit couple of gigabytes rather than an invisible eight, on a host that is already holding 7.752 GiB of experts. --cache-ram 0 disables the cache entirely and is right if you are memory-constrained and your prompts do not share prefixes. With the cache capped, 16 GB of system RAM is arithmetically sufficient; 32 GB is what this recipe asks for, because the gap is the headroom that stops the OS swapping the expert pages you are reading on every token.

-fit turns itself off, and that is what you want here

llama.cpp can size the offload for you: auto-fit is on by default (fit_params = true, common/common.h:476, with the per-device target defaulting to 1 GiB at :481), so "not passing -fit" is not the same as "not using it".

It does not compete with an explicit -ncmoe. The fitter's contract in common/fit.h is that "only parameters that have the same value as in llama_default_model_params are modified", and common/fit.cpp makes the buffer-override case an outright bail: it throws "model_params::tensor_buft_overrides already set by user, abort", which the caller catches and logs as a failed fit before loading with your parameters untouched. So passing -ncmoe gives you the explicit offload and disables the fitter, at the cost of one warning line. Passing -fit off as well is harmless and removes the warning.

Prefer the explicit number anyway, for a second reason: there is an open regression report against --fit-target on a Q4_K_M of another 35B-A3B model — llama.cpp #27171, filed 2026-08-16, still open and labelled bug-unconfirmed when this was written on 2026-08-31, with a fix in flight at PR #27207. Different card, different model — a reason to pin the offload by hand until it lands, not a claim about this pair.

Results

  • Speed: omitted, and not because nobody has measured the model. Two quantisers publish throughput for Apodex 1.1 mini and neither number can travel to this page. ghazni101/Apodex-1.1-mini-MQ4RP reports 226 tok/s on an RX 7900 XTX — gfx1100, and measured from a .mq4r file under hipfire, a separate Rust engine, so it is neither this file, this runtime, nor this vendor. kingjones777 publishes a four-rung ladder — 63.30, 45.05, 32.54 and 64.87 tok/s — every rung of which is measured with full GPU offload (-ngl 999) on a Ryzen AI MAX+ 395 unified-memory APU under a ROCmFPX fork: a machine with no expert offload and no PCIe hop, which is the entire subject of this page. The top of that range is also unsettled on its own card's terms — the STRIX_LEAN build's "Speed — full offload" section is a one-row table holding a dash under a line reading "Full-offload speed being measured on an idle box, card will be updated.", thirty lines above the shared variants table that prints 64.87 for that same build, under a caption reading "A dash means I haven't measured that one yet — I won't put a number in a card I didn't measure." See Troubleshooting for the three partial-offload figures on those same cards, which are the ones a reader of this page is most likely to misread. Nor is there a figure anywhere else I looked. The space searched: the model cards of all 22 HuggingFace repositories matching Apodex-1.1-mini across 11 namespaces (re-enumerated 2026-08-31 — up from the 21/10 counted the previous morning, so this decays daily); the llama.cpp issue tracker, where apodex returns 0 results while qwen35moe returns 273 on the identical query shape, so the zero is about the term and not the search; and our own catalogue, where /check/apodex-1-1-mini/rtx-4080-super returns verdict: unknown with zero benchmarks. There is no throughput figure for this file, on this runtime, at any -ncmoe setting, on any card. If you run it, please contribute the numbers.
  • VRAM usage: 13.546 GiB derived working set at -ncmoe 17 and 131,072 tokens with q8_0 KV, clearing this page's 13.917 GiB ceiling by 0.371 GiB, with 7.752 GiB of expert weights in system RAM. Dropping to -ncmoe 14 at 32,768 tokens gives 13.880 GiB and 6.422 GiB respectively. See /check/apodex-1-1-mini/rtx-4080-super for live data as it lands.
  • Quality notes: the vendor publishes agentic evaluation scores on the model card, but all of them are run in Apodex AI's own harness and several of the benchmarks are the vendor's own creations, so they are a vendor claim rather than an independent measurement and are not reproduced here. The lead quant is imatrix-calibrated — the header names its calibration corpus and records 510 imatrix entries over 590 chunks — which is the main reason to prefer it over an uncalibrated conversion at the same nominal tier. Note that the routed experts are already narrow (expert_feed_forward_length 512), which is part of why the sub-4-bit rungs give up more than the file sizes suggest.

For the full benchmark data, see /check/apodex-1-1-mini/rtx-4080-super.

Troubleshooting

unknown model architecture: 'qwen35moe'

Your binary predates LLM_ARCH_QWEN35MOE, which arrived in (b7989, b7990] — so this error means a genuinely old build, not a merely recent one. Check llama-server --version against the release list and move to b10630 or newer.

It loads fine, then runs out of memory when a long prompt lands

Expected if -ncmoe is too low for the context you asked for. Loading allocates the weights and the full KV cache, but the compute buffers grow with the batch, and prompt processing additionally needs room for the expert weights it is copying to the card. Raise -ncmoe by two or three — on this file that frees between 0.844 and 1.459 GiB depending on which blocks the steps land on — or drop -c. If the card is also driving a display, start two higher than the table above and check nvidia-smi after load.

Prompt processing is slow and the GPU looks idle

This is the expected shape of the offloaded path rather than a fault, and there are three things to try before suspecting the model.

Confirm mmap is off (--load-mode none); llama.cpp prints a warning about it and the 16 GB reporter cited above measured a gain on both phases from following it. Check what your PCIe link negotiated, since prompt processing is the phase that spends it — this is a property of your card and your slot. And raise -b / -ub, which is the largest lever that reporter found: at a fixed -ncmoe 22 and Q8 KV on a 48.5k prompt he swept 1024/512 → 704.99 t/s, 2048/1024 → 1008.24, 4096/2048 → 1104.15, 8192/2048 → 1524.28, 16384/2048 → 1510.35 and 4096/4096 → 808.45 (#25859). Read that carefully before expecting it: his baseline is below the 2048 default on -b and exactly at the 512 default on -ub, so a reader starting from defaults already has more logical batch than his 705 row; his nearest at-or-above-default row makes the gain about 1.5×; the 16384 row is worse than 8192; and an oversized physical ubatch was actively harmful. It is a sweep, not a direction, and it costs compute buffer that this page's 1,024 MiB reserve has not been sized for — raise them and re-read nvidia-smi rather than assuming. Different card, different model, different backend.

--no-op-offload keeps the expert matmuls on the CPU in both phases, which removes the transfers entirely. It is a real trade, untested here, and one flag to try if your link is the constraint.

I found a number saying this technique gives one token per second

You found kingjones777's partial-offload rows, and they are the most misleading figures a reader of this page can meet — not least because one of them contains the number 17.

Three of that publisher's cards carry a partial-offload table alongside the full-offload ladder — "so I functionally checked with partial offload instead": 1.183 t/s, 0.292 t/s and 0.163 t/s. The COHERENT card states the configuration in its own table — "17 / 40 layers on ROCm0", "rest CPU-mmap (-ngl 17 -c 2048)" — on a box that at test time was serving "8 live llama-server seats" holding about 107 GiB of unified memory, leaving him roughly 16 GiB. -ngl 17 is not -ncmoe 17. The first offloads layers, so every tensor of blocks 17–39 including attention and the recurrent path streams from CPU-mmapped host memory; the second offloads only the routed experts of blocks 0–16 and keeps all forty layers' attention, mixers and state on the card. They are opposite configurations that happen to share an integer.

The author retracted the figures himself: "My first published number (1.183 tok/s) was measured with PARTIAL offload on a box already serving 8 models — that was my harness's fault, not the model's." His own caveat on the table is equally clear: the figures are limited by streaming the "CPU-resident layers, not by the ROCm path". Nothing about them bounds this recipe.

Generation looks like it collapses at long context

Measure it with llama-server's own predicted_per_second rather than by dividing generated tokens by total request time. On this path prefill is bus-bound and slow while decode is not, so wall-clock division makes decode look like it collapsed when only prefill did — and that exact error produced llama.cpp #27623, "decode throughput collapses ~25x at context", on a qwen35-family hybrid GDN model. The headline is retracted by its own reporter"My measurement metric was flawed." — and the reason is the metric: "The numbers in my matrix were completion_tokens / total request time, which includes prompt processing". The issue is still open with the retraction as its last comment, so a reader searching their own symptom meets the alarming title first. Cite the retraction, never the headline.

Real decay with depth does exist and is priced above — the 2.32× fall from 4k to 128k in What the SUPER badge buys is what it looks like. A 25× cliff is a measurement artefact.

CUDA error: an illegal memory access was encountered

llama.cpp #26609 (open, 4 comments, updated 2026-08-29) reports exactly this in ggml_backend_cuda_synchronize, deterministically"reproduced 5+ times across two builds" — on qwen35moe under partial expert offload, on the second request of a specific two-request sequence, and it "disappears with --flash-attn off".

Read the scope carefully, because it is not what the original report's hardware line suggests. The reporter's box is an RTX 5070 12 GB on Windows; a second commenter reproduces the same signature on an RTX 3070 Laptop GPU 8 GB on Linux. Two architectures, two capacities, two operating systems. So this is a qwen35moe-under-expert-offload defect rather than a Blackwell one or a 12 GB one — which makes it relevant to this page, and equally makes it not this card's problem. Neither the issue body nor any of its four comments reports it on Ada or on a 16 GB card. It also is the configuration this page documents in every other respect: partial expert offload, one slot, q8_0 KV, FlashAttention on.

If you have to turn FlashAttention off

The workaround #26609 offers is expensive here in a way that is easy to miss, and both halves are worth knowing before you reach for it.

-fa off does not force the whole cache to f16 — only the V half. At b10666 src/llama-context.cpp rejects a quantised V outside FlashAttention ("quantized V cache requires flash_attn to be enabled"), while the K check immediately below is itself gated on FlashAttention not being disabled and therefore never runs. So -ctk q8_0 -ctv f16 is legal: 15,680 B/token rather than 20,480. #26609's own probe G row annotates --flash-attn off as "(+ KV f16, required)", taking both halves to f16 — a widely copyable over-correction.

And turning it off changes the graph, not just the cache. The non-FA path builds an F32 score matrix of [n_kv, n_tokens, n_head], reserved at n_tokens = min(n_ctx, n_ubatch), and this model's head_count is 16 — a value that is in the GGUF header as qwen35moe.attention.head_count and is independently confirmed by attn_output.weight [4096, 2048] ÷ key_length 256. (Do not derive it from blk.N.attn_q.weight [2048, 8192], which divides to 32 because the q projection carries a concatenated gate; that doubles every cell below.) So:

F32 score matrix-ub 512-ub 256-ub 128-ub 64
-c 327681.000 GiB0.5000.2500.125
-c 655362.000 GiB1.0000.5000.250
-c 1310724.000 GiB2.0001.0000.500

At -c 131072 even -ub 128 consumes this page's entire 1,024 MiB compute reserve on its own, before the mask, the softmax or expert staging — and this recipe's own configuration is already 14.132 GiB with -ctk q8_0 -ctv f16 at that context, over the ceiling before any of it. So the fallback is not available at a full window on 16 GB at -ncmoe 17. The supported move is to change the context, not the ubatch: -c 32768 -ub 256 -ctk q8_0 -ctv f16 at -ncmoe 17 leaves about 0.72 GiB after the score matrix. If you need the full window without FlashAttention, you also need a deeper -ncmoe and -ub 64, and at that point you are trading prefill throughput for a workaround to a bug nobody has seen on this card.

⚠️ This table is arithmetic, and #26609's matrix does not test it: every 131072 row in that thread ran with FlashAttention on (which is the bug being reported) and both off rows are at 32768. Nobody has run -fa off at 131,072 under offload. The bound is computed; the cell is untested.

System thrashing, or resident memory climbing request after request

Two different causes with the same symptom. If it climbs and plateaus, that is the prompt cache filling to --cache-ram, which defaults to 8192 MiB — cap it, as the command above does. If it does not plateau, your system RAM is simply short: at -ncmoe 17 more than a third of the file lives in RAM permanently, on top of the OS.

The server downloads an extra file you did not ask for

If you launch with -hf bartowski/apodex_Apodex-1.1-mini-GGUF instead of a local -m path, llama.cpp resolves a projector sibling as well — find_best_mmproj matches any sibling filename containing mmproj, and this repo ships two. Pass --no-mmproj, or use an explicit -m path as above.

Should you turn on the MTP head?

The GGUF carries a working multi-token-prediction block (nextn_predict_layers 1) and mainline llama.cpp implements it behind --spec-type draft-mtp (the exact spelling matters; mtp alone is rejected). On this card, under offload, the honest answer is that nobody has measured it and the costs are all on the wrong side of the ledger.

The costs are concrete: block 40's tensors stop being skipped (+0.444 GiB of weights, which -ncmoe 17 does not touch — the flag only covers blocks 0 to 16); the recurrent state is multiplied; and a second, single-layer KV cache is allocated for the draft context, f16 by default and untouched by your -ctk. Against a 0.371 GiB margin the weights alone do not fit without giving up a step of -ncmoe.

The benefit is unknown and the nearest evidence is discouraging. unsloth's published guidance for this class of model is that "dense models are much more accelerated with MTP (1.4-2x) vs MoE models (1.15-1.25x)" (Qwen3.6 docs) — so 1.15–1.25× at best, before offload is taken into account. And under offload even that goes. An RTX 3060 owner running --n-cpu-moe 25 on a different model of this architecture, with the MTP flags on, reports "Absolutely same decoding speed as on ordinary Q4_K_M GGUF" (discussion #18 on that model's GGUF repo — a community thread, no org member in it). That is at least consistent with the mechanism this page derives: if roughly half the decode step is the CPU reading experts out of DIMMs, drafting more tokens per step does not help, because the bottleneck is not the GPU waiting on itself. Try it if you like; do not budget VRAM for a speed-up you have not seen on your own machine.

Choosing a different quant publisher

Conversions of this model are still appearing: a Hub enumeration on 2026-08-31 returned 22 repositories matching Apodex-1.1-mini across 11 publishers, one namespace more than the same query returned the previous morning. Re-enumerate before you decide, and check the byte count and the file list rather than the tier name — across publishers a file named Q4_K_M spans more than a gigabyte for the same nominal tier. Within bartowski's own ladder the rungs adjacent to the lead are Q4_K_S at 19.619 GiB and IQ4_XS at 17.955 GiB; each is worth roughly two and six steps of -ncmoe respectively, which buys back host RAM rather than VRAM.

You wanted the vendor's own quantised weights

Apodex AI publishes -NVFP4, -GPTQ-Int4 and -FP8 repos, but they are vLLM/SGLang safetensors rather than GGUF and llama.cpp cannot load them at all. They are also much too large for this card before anything else is counted — 22.548 GiB and 22.958 GiB of weights for the first two, against 16 GiB — and the -NVFP4 repo is not a 4-bit checkpoint anyway: its hf_quant_config.json declares quant_algo: MIXED_PRECISION over 290 quantized layers, of which 250 are FP8 and only 40 — the routed experts — are NVFP4. Their own Quick Starts launch on a single GPU: --tp-size 1 on both -NVFP4 and -GPTQ-Int4, --tp 1 on -FP8; the --tp 8 and --tensor-parallel-size 8 lines belong to the unquantised base repo alone. Separately, nothing in the Ada feature set accelerates FP4 — that is Blackwell silicon — so even a loadable NVFP4 build would buy this card nothing. For one RTX 4080 SUPER the GGUF route above is the path.

No Ollama tag

There is no Ollama library entry for this model: ollama.com/library/apodex, /apodex-1.1-mini and /apodex1.1 all return 404 (checked 2026-08-31), while ollama.com/library/qwen3 returns 200 on the identical request shape, so the 404s are about the model and not about the probe. Use llama.cpp directly. If a tag appears later, please tell us.

Is there anything reported against this card specifically?

One thing, and it is worth naming precisely so you can rule it out. Searching the llama.cpp tracker for this card is itself a trap: 4080 matches on the issue number — issue #4080 is "Improve yaml log escaping" — so a naive query returns unrelated hits. Enumerating the results of a "4080 Super" query by hand gives exactly one issue that is genuinely about this board: #20651, "Qwen3.5 27B output incoherent when running on rx 7900 xtx + rtx 4080 super with vulkan backend", filed 2026-03-16, closed as completed.

It is a mixed-vendor multi-GPU Vulkan report — hardware "Ryzen 9 7900 + rtx 4080 super + rx 7900 xtx", launched with -dev VULKAN0,VULKAN1, and working correctly on the AMD card alone — and its reporter confirms it fixed: "After updating to b8394 (which includes #20518), the issue is fixed for me." Nothing about it applies to a single-card CUDA build five thousand releases later. There is no open llama.cpp issue naming an RTX 4080 SUPER as of 2026-08-31, and none at all touching this card under --n-cpu-moe.


What this page does not know

Stated as a list because the arithmetic above is dense enough to read as more certain than it is, and because every framing sentence on this page was written to be consistent with it.

  1. No number here was measured on an RTX 4080 SUPER running Apodex 1.1 mini. /check/apodex-1-1-mini/rtx-4080-super holds zero benchmarks. Every VRAM figure is arithmetic over the artifact's tensor table; every timing is a bandwidth roofline.
  2. The 736 GB/s is a derivation from a board partner's memory-speed figure, not a vendor bandwidth publication — NVIDIA publishes none for either 4080 variant. The method reproduces NVIDIA's own published figures for neighbouring cards exactly, which is the strongest thing that can be said for it.
  3. The 13.917 GiB ceiling contains two reservations that are not measurements: 1,109 MiB borrowed as an absolute quantity from a 12 GB card's measured run, and llama.cpp's own 1,024 MiB default fit margin standing in for a compute-buffer term nobody has sized on this architecture.
  4. The decode split assumes both memories run at their peak rate and that the two halves serialise. The nearest comparable configuration anyone has published reports throughput in the tens where this page's ceilings are in the low hundreds, so the gap between the bound and reality is large and unquantified here.
  5. The measured rows in What the SUPER badge buys are other models, fully resident, with no host half at all. They calibrate the card side of the roofline; they say nothing about your DIMMs.
  6. This page asserts no PCIe lane count. Read yours with nvidia-smi.

If you run this pair and record the startup buffer sizes and a predicted_per_second, that measurement would replace most of this page, and it is the single most useful thing anyone could send us about this card.

common questions
How much VRAM does Apodex 1.1 mini need?

About 16 GB — the minimum this recipe targets.

Which GPUs is Apodex 1.1 mini tested on?

RTX 4080 Super (16 GB).

How hard is this setup?

Advanced — follow the steps above.

next