Choosing a local LLM for a 16 GB RTX 5080, part 1: what the bake-off shows so far

Published

Project article; see sources and editorial standards.

Update, 2026-10-14: Part 2 is published, with the final choice, the launch command and the harness scripts. It corrects one statement below: the slow dense-model results here came from our server flags (automatic fit with four request slots), not from a hardware limit. Fully on the GPU with one slot, Qwen3.8-27B decodes at about 50 tokens per second. We have not re-run Qwen3.6-27B or Devstral Small 2 that way.

We set out to find the best open-weight model for two jobs on a new Fedora box with a single 16 GB RTX 5080: agentic coding, and concept research with our /dr, /rabbithole and concept-family skills. We also wanted the fastest stable way to serve it.

The interim answer is that two models lead and neither wins outright. gpt-oss-20b is the fastest by a wide margin and has the best average coding score. Qwen3.6-35B-A3B with thinking switched off writes better research claims and concept maps, but it invents details when it answers questions from sources. Bigger and slower models did not win anything. One open model that postdates our shortlist, Qwen3.8-27B, is being added and could change the picture. Which model becomes the default, and how it is served, is Part 2.

This part covers what we ran, what the numbers say, and the places where our own first-pass metrics misled us.

The box and the question

Part Spec
GPU NVIDIA RTX 5080, 16 GB (about 15.6 GiB usable)
CPU Intel Core i7-12700K, 20 threads
RAM 62 GB DDR5, running at 4000 MT/s
OS and toolchain Fedora Linux, CUDA toolkit 13.4
Main engine llama.cpp build 77757a3 (2026-10-12), compiled for the RTX 5080 (CUDA architecture sm_120)

A 16 GB card changes the question. Most strong open models are larger than the card, so the real contest is between models that fit, mixture-of-experts (MoE) models that can spill their expert weights into system RAM at a modest speed cost, and dense models that cannot do either well.

We also kept one hard requirement from the existing setup: our document indexer uses the mxbai-embed-large embedding model through Ollama. Any change of chat model must not break that, and an embedding comparison is part of the bake-off.

The ten candidates

We chose ten current GGUF builds from the Unsloth quantization repositories. This was our own shortlist of recent releases, not an exhaustive survey. One quantization per model, so quantization effects are not separated from model effects.

Model Build tested File size Shape
gpt-oss-20b UD-Q4_K_XL 11.1 GiB MoE, fits in VRAM
gpt-oss-120b MXFP4 59.0 GiB MoE, mostly in RAM
Qwen3.6-35B-A3B UD-Q4_K_M 21.1 GiB MoE, partly offloaded
Qwen3.6-27B IQ4_XS 14.4 GiB dense
Qwen3-Coder-30B-A3B Q4_K_M 17.3 GiB MoE, partly offloaded
Qwen3-Coder-Next IQ4_XS 39.7 GiB mostly in RAM
Gemma 4 26B-A4B (QAT) UD-Q4_K_XL 13.3 GiB MoE
GLM-4.7-Flash Q4_K_M 17.1 GiB MoE, partly offloaded
Nemotron 3.5 Lightning 30B-A3B UD-Q4_K_S 22.8 GiB MoE, partly offloaded
Devstral Small 2 24B UD-Q4_K_XL 13.5 GiB dense

A gap in the shortlist

We built this list on 2026-10-12 and it is already behind. We were asked whether Qwen3.7 is in these tests. It is not, and it cannot be: the Qwen3.7 Max and Plus models are hosted-only, and as of 2026-10-13 the Qwen organization on Hugging Face lists no Qwen3.7 repositories. The open successor is Qwen3.8-27B, a dense 27B model released in August under Apache-2.0, with community-quantized GGUF builds and separate multi-token-prediction draft heads. We missed it. It is now queued, and the results go in Part 2. The same Qwen3.8 release also includes a 180B model and a 2.4T model, both much larger than a 16 GB card plus 62 GB of RAM handles comfortably, and we did not test them. Other new open models we saw trending on Hugging Face and have not tested include GLM-5.3, JetBrains’ Mellum 2.1 and LiquidAI’s LFM2.5-24B-A2B.

Where a model did not fit, we let llama.cpp’s automatic fit (--fit on) decide how many layers and experts go to the GPU. Forcing all layers onto the GPU with -ngl 99 caused a CUDA out-of-memory crash on the 22 GB file we tried it on.

How we tested

Three stages, each meant to be cheap to fail.

Stage 1: smoke and speed. Nineteen quick checks per model: short reasoning and instruction-following problems, single, multiple and unnecessary tool calls, eight short coding problems that run against assertions, and one long-context retrieval of a planted fact from roughly 12,000 tokens. Then a speed sweep of decode and prompt-processing rates at about 512, 4k and 16k tokens of context.

Stage 2: real work.

Stage 3: servers. The same model behind several engines, measured in the same harness.

Stage 1: speed and smoke

Model Smoke (of 19) Decode tok/s at 512 / 4k / 16k Prompt tok/s at 4k / 16k
gpt-oss-20b 6 of 8 ¹ 186 / 146 / 132 10,296 / 8,217
Qwen3-Coder-30B-A3B 18 72 / 68 / 48 1,727 / 1,614
Gemma 4 26B-A4B 18 71 / 67 / 69 1,954 / 2,040
GLM-4.7-Flash 17 54 / 50 / 46 1,065 / 1,023
Qwen3.6-35B-A3B 16 51 / 53 / n/a 1,098 / n/a
Nemotron 3.5 Lightning 17 49 / 49 / 48 633 / 679
Qwen3-Coder-Next 17 41 / 41 / 40 564 / 588
gpt-oss-120b 18 26 / 28 / 28 316 / 375
Qwen3.6-27B (dense) not run 11 / 10 / n/a 942 / n/a
Devstral Small 2 24B (dense) 14 10 / 8 / n/a 864 / n/a

¹ gpt-oss-20b ran an earlier eight-check version of the smoke set. The nineteen-check set was run on it later in the server shoot-out below, where it scored 18 of 19 on both LM Studio and Ollama.

Stage 1 used default batch sizes and a 32k context (16k for the dense models). Later reruns with larger batch settings changed some numbers noticeably. Qwen3.6-35B-A3B went from about 51 to about 64 tokens per second at short context once we raised the micro-batch to 2,048. We did not isolate whether the cause was the batch size or the different GPU/CPU split that the automatic fit chose, so treat Stage 1 speeds as a first pass.

Three things stood out.

  1. gpt-oss-20b is in a class of its own on speed. It is the only model here that fits entirely in VRAM with room for context, and it decodes more than twice as fast as every other model that was usable in the agent loop.
  2. The dense models are too slow on this card. Qwen3.6-27B and Devstral decode at 8 to 11 tokens per second even on short prompts, and Qwen3.6-27B dropped to 4.7 tokens per second deep into the ninth coding task. We stopped that coding run after about 2 hours 14 minutes with no score. Devstral did finish: 3 hours 43 minutes for a 0.782 score. Our explanation is that a 14 GB file plus context and compute buffers does not fit in 16 GB, so part of every token runs on the CPU. A dense model has to touch all of its weights for every token, while an MoE model touches only a few experts. We did not run an experiment to prove that split is the cause.
  3. gpt-oss-120b is capped by system memory. At 26 to 28 tokens per second, decode appears limited by DDR5-4000 bandwidth rather than by the GPU. A sweep of five server flag combinations on it (batch sizes, thread counts, a single slot) never beat the first configuration’s 25.0 tokens per second on this measure.

Models that think by default are expensive in this harness even when they are correct. Qwen3.6-35B-A3B took 648 seconds for the nineteen checks and scored 16, while Qwen3-Coder-30B-A3B took 54 seconds and scored 18.

Stage 2: coding

Each row is a full ten-task run, regraded test by test.

Model and setting Tasks fully passed Mean fraction of hidden tests Hidden tests passed (of 374) Wall time
gpt-oss-20b, run 1 / 2 / 3 6 / 5 / 8 0.864 / 0.960 / 0.988 323 / 355 / 368 22 to 27 min each
Qwen3.6-35B-A3B, thinking off, run 1 / 2 / 3 3 / 5 / 4 0.906 / 0.855 / 0.877 337 / 276 / 269 22 to 26 min each
Gemma 4 26B-A4B, thinking on 6 0.976 354 2 h 34 min, 3 empty-response errors
gpt-oss-120b 5 0.863 347 2 h 8 min
Qwen3-Coder-30B-A3B 2 0.830 312 35 min
Nemotron 3.5 Lightning 2 0.788 217 80 min, 1 error
Devstral Small 2 24B (dense) 3 0.782 305 3 h 43 min, task 9 tests timed out
Qwen3.6-35B-A3B, thinking on 3 0.765 239 1 h 44 min, 3 errors
Qwen3-Coder-Next 2 0.670 160 28 min
GLM-4.7-Flash 1 0.670 208 87 min
Gemma 4 26B-A4B, thinking off 3 0.666 157 31 min
Qwen3.6-27B (dense) aborted none none stopped after 2 h 14 min

What this does and does not show:

Stage 2: research with /dr

We ran gpt-oss-20b and Qwen3.6-35B-A3B (thinking off) three times each on the same corpus, then had Claude subagents judge shuffled, anonymous outputs on a 1 to 10 scale. Each of the five judged items covered all six outputs.

Judged item gpt-oss-20b Qwen3.6-35B-A3B
Claims file: speculative decoding with a conflicting forum post 6.33 8.00
Claims file: Medusa and EAGLE drafters 6.00 7.67
Claims file: deploying in llama.cpp and vLLM 6.67 7.33
Concept-family map 2.67 6.00
Questions answered from the sources (7) 7.00 4.33

The split is real and it has a clear pattern.

Gemma 4 was also tried on /dr. With thinking on, only one of three claims files was valid JSON because reasoning exhausted the output cap, and it took 731 seconds. With thinking off, the claims files were valid for all three concepts in 73 seconds, but it scored 2 of 7 on the questions, with empty final answers on three of them. We dropped it from the research shortlist.

Limits of this comparison: the judge is itself a Claude model judging open models, there are only three outputs per model per item, one judge pass per item, and the raw concept-family output was truncated to 6,000 characters before judging (the judges noted it for three of six outputs). We treat the direction as informative and the decimals as noise.

Servers: one model, several engines

For the engine comparison we used gpt-oss-20b, because it is the one model that can load in every engine, including the ones that need a model to fit entirely in VRAM. Each engine served it with a 65,536-token context. The desktop session (a browser, other apps and a resident Ollama process for the indexer) was holding roughly 1 to 2 GB of VRAM during these runs. That affects every row, and a clean headless box would have a little more room. The VRAM column is whole-card usage as reported by nvidia-smi, so it includes that share.

Engine Single-stream decode tok/s (512 / 4k / 16k) Prompt tok/s (512 / 4k / 16k) Card VRAM in use Smoke (of 19) Code subset (4 tasks)
LM Studio, headless through its lms command 201 / 194 / 184 6,083 / 10,719 / 9,618 14,176 MiB 18 3 of 4, 324 s
Ollama 0.40.3 137 / 135 / 129 913 / 6,240 / 7,088 14,923 MiB 18 3 of 4, 441 s
llama.cpp 186 / 146 / 132 (earlier run, other settings) 5,652 / 10,296 / 8,217 not recorded rerun pending see coding table
ik_llama.cpp out of memory at -ngl 99 -ub 1024; rerun with --fit pending
vLLM 0.31.0 does not fit
SGLang 0.5.21 does not fit

How to read it:

MoE offload on a model that does not fit

For a model that needs system RAM, we compared llama.cpp with ik_llama.cpp, which describes itself as a llama.cpp fork with additional quantization types and improved performance, on Qwen3.6-35B-A3B with automatic fit and a 2,048 micro-batch.

Engine Decode tok/s (512 / 4k / 16k) Prompt tok/s (512 / 4k / 16k) Card VRAM in use
llama.cpp 64 / 63 / 61 982 / 2,391 / 2,609 14,689 MiB
ik_llama.cpp 72 / 70 / 67 789 / 3,344 / 3,593 15,059 MiB

ik_llama.cpp decodes 10 to 12 percent faster and processes long prompts about 38 to 40 percent faster, but it is about 20 percent slower on a 512-token prompt. It also uses about 370 MiB more, leaving roughly 1.2 GiB of the 16,303 MiB card free, with the desktop’s share included. Whether that trade is worth a second engine in the stack is for Part 2.

Bionic

LM Studio now ships a separate agent app called Bionic, which its documentation describes as an AI agent for open models, not as an inference server. We wanted to test it, but when we launched its AppImage it opened a desktop window, and we found no headless serving mode we could script. Our use case is driving a model from other agentic programs, so we dropped it as a test target. We have no measurements for it, and that says nothing about how good it is for its intended use.

Engines we did not test

We did not run ExLlamaV3 with TabbyAPI, KTransformers, TensorRT-LLM, or the llama.cpp wrappers (KoboldCpp, LocalAI, llamafile, Jan, Docker Model Runner, ramalama). That was a judgment call about effort, not a measurement. If one of those deserves a run on this hardware, it is cheap to add. llama-swap, a proxy that loads several models behind one port, is a deployment layer rather than an engine, so it belongs in the service-setup stage.

The embedder baseline

The existing indexer embeds with mxbai-embed-large through Ollama. To see whether anything is better, we built a retrieval test from 14,800 code-symbol chunks from our own index and 800 docstring queries, each with one correct chunk. The baseline scores:

Metric mxbai-embed-large
Recall@1 0.290
Recall@5 0.595
Recall@10 0.685
MRR@10 0.417

Those scores used mxbai’s query prefix. Without it, the same model scored Recall@1 0.2875, Recall@5 0.609, Recall@10 0.703 and MRR@10 0.421, so the prefix does not help on this set. We truncate each chunk to 480 tokens, matching the model’s 512-token limit. Candidates queued against it: Qwen3-Embedding at 0.6B and 4B, EmbeddingGemma-300M and nomic-embed-code. Switching embedders would force a full re-embed of the index, so a candidate has to beat about 0.70 Recall@10 clearly to be worth it. Results are in Part 2.

What broke along the way

These mistakes changed conclusions, so they are part of the result.

What is still running

At the time of writing:

  1. Qwen3.8-27B: a speed and memory sweep (including its draft heads), then one coding run and three /dr runs with thinking off, if it decodes fast enough to be usable.
  2. The llama.cpp baseline and ik_llama.cpp with --fit in the engine harness.
  3. The embedding bake-off against mxbai-embed-large.
  4. Tuning of the two leaders. Speculative decoding with multi-token prediction on Qwen3.6-35B-A3B, batch-size and quantized-KV-cache sweeps on gpt-oss-20b, and gpt-oss-20b at high and low reasoning effort on both coding and /dr, to see whether more reasoning closes its research gap.

Then the work that decides the outcome: a test serving both leaders behind one port, a 30 to 60 minute mixed-load stability soak, and installing the winner as a service.

Part 2

Part 2 will give the final choice of model and server, the exact launch command, the tuning and embedding results above, the stability soak, and the reasons the other candidates lost.

If you want to repeat this on your own card, the method matters more than our scores: hidden tests graded test by test, at least three runs per configuration, blind judging for synthesis tasks, and one quantization at a time. The harness scripts are published with Part 2.