For single-user chat on an MSI GeForce RTX 3060 Ventus 3X 12G, llama.cpp and Ollama deliver nearly identical throughput — Ollama wraps llama.cpp's own server binary, so the gap for one concurrent user is usually a few percent at most. Raw llama.cpp with hand-tuned flags can edge ahead, but Ollama's defaults are close enough that most users will never feel the difference. The real divergence is in usability and control, not raw tok/s.
Ollama is a wrapper around llama.cpp — when does the wrapper cost you throughput?
The local-LLM ecosystem has split into two camps that look, on the surface, like genuine competitors: Ollama, the polished launcher with a model registry and a REST API built for developers, and llama.cpp, the bare-metal C++ inference engine that both tools ultimately depend on. The confusion is understandable: benchmarks circulate on r/LocalLLaMA showing tok/s differences between the two, and product pages position them as alternatives.
The architectural reality is simpler. Ollama ships a Go binary that, at inference time, spawns a platform-optimized llama.cpp server process — either the bundled llama-server or the equivalent dynamic library. When you send a chat request to Ollama's API on port 11434, the request is proxied to that internal llama.cpp process. The GPU computation path is identical. The question is not "which inference engine is faster" but rather "does Ollama's bundled llama.cpp build and its default settings cost you anything relative to a self-compiled, hand-tuned invocation?"
For a single-user chat workload on a 12GB GPU, the answer is: occasionally, and by a small margin. Overhead sources exist — Ollama's model-keep-alive manager, conservative default KV-cache sizing, and the fact that its bundled build may lag a few weeks behind llama.cpp's main branch — but none of those sources are catastrophic. The RTX 3060's 360 GB/s memory bandwidth is the real throughput ceiling, and both tools bump against it equally once the model is fully offloaded.
Where the gap opens is in edge cases: very long context windows, aggressive quantization of the KV cache, or enabling flash attention before Ollama exposes it in its official build. Power users who want those levers, or who need the absolute latest llama.cpp commit because it fixed a bug with a specific model architecture, will reach for raw llama.cpp. Everyone else — developers prototyping agents, home users running personal assistants, hobbyists testing new models — will find Ollama fast enough and significantly easier to operate.
Key Takeaways
- Ollama bundles a specific llama.cpp build; on a single-user 3060, throughput is typically within 3-6% of a manually compiled llama.cpp run.
- The biggest performance lever on any 12GB card is full GPU offload (
-ngl 99in llama.cpp; automatic in Ollama when VRAM fits the model). - Flash attention (
--flash-attn) and KV-cache quantization (--kv-cache-type q8_0) are the two llama.cpp flags Ollama does not yet expose as first-class options, and they matter at long context. - For Llama 3.1 8B at Q4_K_M, a 12GB card fits the entire model in VRAM with room for a 4k–8k context window.
- Qwen 2.5 14B at Q4_K_M fits in 12GB but leaves less headroom; context above 4k requires KV-cache quantization.
- Use Ollama if you want zero-friction model management; use raw llama.cpp if you are chasing every tok/s or need features ahead of Ollama's release cycle.
What is the relationship between Ollama and llama.cpp?
Ollama is not a reimplementation of inference. It is a distribution layer that bundles a precompiled llama.cpp binary (or shared library) alongside a Go-based daemon that handles model downloads, GGUF format storage, template rendering, stop-token injection, and a REST API compatible with the OpenAI chat completions spec. When you run ollama run llama3, the Go daemon locates the GGUF file, constructs a llama.cpp server launch command with appropriate flags, and pipes your conversation through it.
This architecture has practical consequences:
Version lag. Ollama ships its own compiled llama.cpp builds, which typically trail the upstream repository by days to weeks. A new quantization format, a bug fix for a specific model family, or a performance patch may be available in llama.cpp main before Ollama's next release bundles it.
Fixed defaults. Ollama chooses default values for context length, batch size, and GPU layer offload based on the detected GPU. Those defaults are conservative — they prioritize stability across a wide hardware matrix rather than squeezing the last tok/s from a specific card.
No direct flag pass-through. As of mid-2026, Ollama does not expose all llama.cpp server flags to end users. You can override context length and a few other parameters via Modelfile or API options, but flags like --flash-attn or --kv-cache-type require running llama.cpp directly. The Ollama GitHub issue tracker (github.com/ollama/ollama) has open requests for exposing these flags, and the situation evolves with each release.
Automatic model management. Ollama's registry handles GGUF hosting, version pinning, and template metadata. Pulling a new model is ollama pull qwen2.5:14b. With raw llama.cpp, you source GGUF files from Hugging Face, manage filenames, and set chat templates manually.
Feature-delta table: build flags, GPU offload control, API, quant support, update cadence
| Feature | Ollama | llama.cpp (direct) |
|---|---|---|
| Build flags | Vendor-chosen at release time | User-compiled; full control over CUDA arch, AVX2, Metal |
| GPU layer offload | Automatic (fills VRAM); no manual -ngl | Manual -ngl N; set to 99 to push all layers to GPU |
| Flash attention | Not exposed in UI (as of mid-2026) | --flash-attn flag; cuts KV memory significantly |
| KV-cache quantization | Not exposed | --kv-cache-type q8_0 / q4_0; critical for long context |
| API compatibility | OpenAI chat completions + /api/generate | llama-server exposes OpenAI-compatible endpoint |
| Quantization formats | GGUF (Q2_K through Q8_0, IQ series) | Same; access to new formats as soon as llama.cpp merges them |
| Model management | Built-in registry; ollama pull <name> | Manual GGUF download; Hugging Face CLI or huggingface-hub |
| Chat template support | Built-in per-model template injection | Manual --chat-template or jinja2 template file |
| Update cadence | Releases lag llama.cpp main by days–weeks | Build from git HEAD; same-day access to commits |
| Concurrency model | Keep-alive daemon; queues requests | Single or multi-session depending on server flags |
| Installation | Single binary install script | CMake build; ~5 min on a modern system |
| Windows support | Official installer | Works; CUDA build requires CUDA toolkit |
Does Ollama add measurable overhead over raw llama.cpp on a 12GB GPU?
The overhead question is real but bounded. Community measurements from r/LocalLLaMA threads and llama.cpp GitHub discussions consistently show that for a single concurrent user, the tok/s delta between Ollama and a manually invoked llama.cpp server is small — typically 2-6% — and varies with model, quantization, and the specific llama.cpp commit Ollama has bundled.
The sources of overhead that do exist are:
Daemon and proxy latency. Ollama's Go daemon proxies requests to the internal llama.cpp process. The additional socket hop adds single-digit milliseconds to time-to-first-token, which is not meaningful at human typing speed but shows up in automated benchmarks.
Conservative context defaults. Ollama sets a default context window that may not maximize throughput. A hand-tuned llama.cpp invocation with --ctx-size 2048 when you only need 2k context will use less KV memory than Ollama's default 4k, leaving slightly more headroom for batch prefill.
Model keep-alive management. Ollama keeps models loaded in VRAM between requests, which is efficient for interactive use. However, the keep-alive logic can in some configurations reload the model between sessions, adding a few seconds of latency that has nothing to do with inference throughput.
What does not add overhead: The CUDA execution path. Once Ollama's llama.cpp process has the model loaded and layers offloaded to the RTX 3060, the tensor operations run through the same cuBLAS and custom CUDA kernels as a manually launched llama.cpp server. There is no Ollama-specific inference code in the hot path.
The practical conclusion, drawn from community benchmarks and llama.cpp GitHub issue discussions, is that Ollama's overhead on a single-user workload is not the reason to choose raw llama.cpp. The reason to choose raw llama.cpp is access to flags Ollama does not yet expose.
Benchmark table: Llama 3.1 8B-Q4 and Qwen 2.5 14B-Q4 tok/s, both tools, same GPU (cited)
Numbers below are drawn from community-reported results in r/LocalLLaMA threads and llama.cpp GitHub issue benchmarks (as of mid-2026). RTX 3060 12GB (GA106), driver 555+, CUDA 12.x, Windows 11 or Ubuntu 24.04. Individual results vary with CPU, RAM speed, driver version, and thermal state. These are representative ranges, not guaranteed figures.
| Model | Quantization | Tool | GPU Layers | Context | Tok/s (generation) | Source |
|---|---|---|---|---|---|---|
| Llama 3.1 8B | Q4_K_M | Ollama 0.5.x | Auto (33/33) | 4096 | 58-65 | r/LocalLLaMA community reports |
| Llama 3.1 8B | Q4_K_M | llama.cpp (tuned) | 33/33 (-ngl 99) | 4096 | 61-68 | llama.cpp GitHub issue benchmarks |
| Llama 3.1 8B | Q4_K_M | llama.cpp + flash-attn | 33/33 | 8192 | 63-70 | llama.cpp GitHub issue benchmarks |
| Qwen 2.5 14B | Q4_K_M | Ollama 0.5.x | Auto (48/48) | 2048 | 28-34 | r/LocalLLaMA community reports |
| Qwen 2.5 14B | Q4_K_M | llama.cpp (tuned) | 48/48 (-ngl 99) | 2048 | 30-36 | llama.cpp GitHub issue benchmarks |
| Qwen 2.5 14B | Q4_K_M | llama.cpp + flash-attn + q8_0 KV | 48/48 | 4096 | 29-35 | llama.cpp GitHub issue benchmarks |
Key observations from the community data: the Llama 3.1 8B-Q4_K_M gap between Ollama and a tuned llama.cpp invocation is roughly 3-8 tok/s — audible in automated tests, invisible to a human typing at conversational pace. The Qwen 2.5 14B gap is similarly small. The more impactful difference is that llama.cpp with --flash-attn enables longer context on 12GB without out-of-memory errors, which Ollama cannot currently match without the model being reloaded at a shorter context.
Quantization matrix: q2 through fp16 — VRAM on 12GB, tok/s, and quality loss
The RTX 3060 12GB has 12 GB GDDR6. Fitting the entire model in VRAM is the single most important performance factor. Once a single layer spills to CPU RAM, throughput collapses by 60-80% on a typical desktop with DDR4 memory.
The table below covers Llama 3.1 8B and Qwen 2.5 14B, the two models most commonly run on 12GB cards in 2026. VRAM figures include model weights plus a 2k context KV cache; "fits" means enough headroom for interactive use. Quality loss is relative to fp16 (BFloat16) baseline, per perplexity measurements shared in llama.cpp GitHub discussions.
| Quant | Llama 3.1 8B VRAM | Fits 12GB? | Qwen 2.5 14B VRAM | Fits 12GB? | Tok/s impact vs Q8 | Quality loss |
|---|---|---|---|---|---|---|
| Q2_K | ~3.5 GB | Yes (lots of headroom) | ~6.0 GB | Yes | +10-15% | High — noticeable degradation |
| Q3_K_M | ~4.4 GB | Yes | ~7.5 GB | Yes | +8-12% | Moderate |
| Q4_K_M | ~5.2 GB | Yes | ~9.1 GB | Yes (tight) | +5-8% | Low — most users won't notice |
| Q5_K_M | ~6.0 GB | Yes | ~10.5 GB | Yes (very tight) | +2-4% | Minimal |
| Q6_K | ~7.2 GB | Yes | ~12.3 GB | No (spills to CPU) | +1-2% | Near-transparent |
| Q8_0 | ~9.5 GB | Yes | ~16.1 GB | No — CPU required | baseline | Transparent to fp16 |
| fp16 | ~16.0 GB | No — CPU required | ~28.0 GB | No | reference | Reference quality |
The community-consensus sweet spot for a 12GB card in 2026 is Q4_K_M for most use cases: it fits cleanly, delivers near-transparent quality for chat, and leaves enough headroom for a 4k–6k context window. Q5_K_M is the next step up for users who notice subtle fluency differences. Q6_K for Qwen 2.5 14B will overflow 12GB and trigger CPU offload, tanking throughput — stick to Q4_K_M or Q5_K_M for that model.
How to hand-tune llama.cpp flags for a 3060
Three flags matter more than any other configuration choice on a 12GB card. Each addresses a different resource constraint.
-ngl N — GPU layer offload
-ngl (number of GPU layers) tells llama.cpp how many transformer layers to offload to the GPU. Setting it too low leaves layers on the CPU, which forces cross-bus data transfers on every forward pass and crushes throughput.
The correct value for a fully-fitting model is 99 (or the actual layer count — llama.cpp will not error if 99 exceeds the layer count, it simply uses the maximum). For Llama 3.1 8B-Q4_K_M, all 33 layers fit in VRAM. For Qwen 2.5 14B-Q4_K_M at 48 layers, all 48 fit if the context is kept at 2k–4k.
Ollama handles this automatically by probing VRAM at startup. The difference is that llama.cpp lets you set -ngl to a specific partial count if you want to deliberately split a larger model across GPU + CPU — useful for models that nearly fit in 12GB.
--flash-attn — flash attention
Flash attention rewrites the attention computation to operate in smaller tiles that stay in SRAM (on-chip memory), reducing the volume of data moving between VRAM and compute units. The practical effects on a 3060 are: lower peak VRAM consumption during attention (most visible at long context), and modestly faster prefill throughput on long prompts.
Per discussion in the llama.cpp GitHub issue tracker, flash attention enables fitting longer contexts into 12GB that would otherwise OOM. For conversational chat at 2k–4k context, the impact is modest. For applications with long system prompts or multi-turn context accumulation, it is the difference between working and out-of-memory.
Ollama does not expose --flash-attn as a user-facing option in mid-2026. It may be enabled in Ollama's internal build on some platforms; the behavior is not documented and is subject to change.
--kv-cache-type q8_0 — quantized KV cache
The KV cache stores intermediate attention states for each token in the context window. At fp16 precision, a 4k context window for Llama 3.1 8B consumes roughly 500 MB of VRAM; at 8k context, roughly 1 GB. Quantizing the KV cache to q8_0 (8-bit) cuts that footprint by roughly half with minimal effect on output quality for chat tasks. q4_0 halves it again but introduces visible quality degradation in longer conversations.
This combination — full GPU offload, flash attention, q8_0 KV cache — is the standard tuning recipe shared across r/LocalLLaMA for RTX 3060 12GB as of 2026. It enables a stable 8k context for Llama 3.1 8B-Q4_K_M without OOM.
Prefill vs generation and context-length behavior on each
Local LLM throughput has two distinct phases that behave differently on a 3060.
Prefill (also called prompt processing) is where the model processes your entire input — system prompt, conversation history, and the new user message — before generating the first token. This phase is compute-bound and benefits from large batch sizes. On a 3060, prefill throughput for Llama 3.1 8B-Q4_K_M is typically 400–900 tok/s per community benchmarks — fast enough that a 2k-token context processes in 2–5 seconds.
Generation is the autoregressive decode loop, where the model produces one token per forward pass. This phase is memory-bandwidth bound — the 3060's 360 GB/s bandwidth ceiling sets the hard limit. Generation throughput is the number most commonly reported as "tok/s" in benchmark tables (58–68 for 8B-Q4, per the numbers above) and it does not change meaningfully with context length until the KV cache fills VRAM.
The context-length inflection point is where Ollama and raw llama.cpp diverge most visibly. Ollama's defaults cap context at a conservative size, and without --flash-attn or KV quantization, the KV cache grows quadratically with context length. A user running a 16k-context session on Ollama with default settings may hit OOM or VRAM-swapping slowdowns that a llama.cpp invocation with flash attention avoids entirely.
For typical single-user chat sessions — exchanges under 4k tokens — both tools behave identically. The gap opens at 8k+ context, where llama.cpp's flag access provides a meaningful operational advantage.
Which should you run? Verdict matrix
| Scenario | Recommendation | Reason |
|---|---|---|
| New to local LLMs, want something working in 10 minutes | Ollama | Single install script, automatic model downloads, sane defaults |
| Developer building an app against a local OpenAI-compatible endpoint | Ollama | Stable API, model keep-alive, easy swap between models |
| Single chat session, < 4k context, Llama 3.1 8B or similar | Either | Performance difference is below 5%; coin flip |
| Long-context sessions (8k–32k) on 12GB | llama.cpp with --flash-attn + --kv-cache-type q8_0 | Ollama cannot expose these flags; OOM risk on long context |
| Chasing maximum tok/s from the card | llama.cpp, self-compiled, -ngl 99 + flash-attn | Removes Ollama's conservative defaults; matches latest upstream optimizations |
| Need a model the day it drops on Hugging Face | llama.cpp | GGUF available same-day; Ollama registry may lag days–weeks |
| Running multiple models in parallel / hot-swapping | Ollama | Keep-alive manager handles this cleanly; llama.cpp requires process management |
| Automated benchmark pipeline | llama.cpp | Reproducible flag-by-flag control; no daemon state |
| Connecting to Open WebUI, Anything LLM, or similar frontends | Either | Both expose OpenAI-compatible API; Ollama is slightly simpler to configure |
| Raspberry Pi or ARM SBC | llama.cpp | More control over ARM NEON flags; Ollama's binary may not be optimized |
Common pitfalls
Five failure modes that appear repeatedly in r/LocalLLaMA threads and llama.cpp GitHub issues for RTX 3060 setups:
- Setting
-ngltoo low and blaming the GPU. If-nglis 0 or not set, all computation happens on CPU. The RTX 3060 sits idle. Throughput on CPU alone for a 7B model is 3–8 tok/s on a typical desktop CPU — a 10x regression from what the GPU delivers. Always verify GPU utilization withnvidia-smiwhile the model is generating.
- Loading a model that doesn't fit in 12GB and expecting GPU speed. Q6_K of Qwen 2.5 14B, or Q8_0 of Llama 3.1 8B, pushes past 12GB and spills to CPU RAM. The GPU still does some work, but the PCIe bandwidth bottleneck collapses throughput. Check VRAM usage before running.
- Ignoring the llama.cpp build for your CUDA version. Prebuilt llama.cpp binaries are compiled for a specific CUDA compute capability. Running a binary compiled for sm_86 (Ampere) on an RTX 3060 (also sm_86) is fine, but a binary compiled for sm_89 (Ada) will either fail or run slowly. When building from source,
cmakewill detect the correct arch. When downloading prebuilt binaries, verify the CUDA version matches your installed toolkit.
- Not setting
--ctx-sizeto match actual usage. Both tools allocate KV cache proportional to the context size at launch. If Ollama or llama.cpp allocates for 32k context but your conversations are 2k, you are wasting VRAM on cache that will never be used. Set--ctx-size 4096(or lower) to recover that headroom and improve generation throughput.
- Benchmarking with the first request after a cold load. The first inference run after loading a model includes JIT kernel compilation, CUDA graph capture, and memory allocation. These inflate time-to-first-token by 1–3 seconds. Fair benchmarking starts timing from the second request. Ollama's keep-alive daemon means its first-request timing is often misleadingly fast in isolation because the model was already warm from a prior run.
Sample llama.cpp command lines
These are representative invocations for an RTX 3060 12GB, drawn from community configurations shared in llama.cpp GitHub discussions and r/LocalLLaMA. Adjust paths to match your model location.
Minimal interactive chat, Llama 3.1 8B-Q4_K_M:
Server mode with flash attention for long-context sessions:
Qwen 2.5 14B-Q4_K_M, tight VRAM, short context:
Benchmark / throughput test (no server, batch decode):
The -p 512 argument benchmarks prefill on a 512-token prompt; -n 128 benchmarks 128-token generation; -r 3 repeats three times for a stable average. This is the benchmark format used in most llama.cpp GitHub issue performance reports, making it directly comparable to community numbers.
Storage and CPU context
A fast NVMe SSD such as the Samsung 970 EVO Plus NVMe SSD does not change steady-state generation throughput — once the model weights are resident in VRAM, disk is not in the data path. Where NVMe speed matters is model load time: a 5GB GGUF file takes roughly 3–5 seconds to load from a fast NVMe versus 10–15 seconds from a slow SATA SSD. If you switch models frequently or restart the server often, faster storage reduces iteration time.
CPU choice affects prefill throughput more than generation. The AMD Ryzen 7 5700X paired with an RTX 3060 is a common configuration in the local-LLM community — the 8-core Zen 3 core provides enough PCIe bandwidth headroom and CPU-side processing for prefill operations that run on the host. For models that fully fit in VRAM (which the 3060 achieves with Q4_K_M Llama 3.1 8B), the CPU's main contribution to throughput is prompt tokenization and managing the inference loop — neither is a bottleneck on a modern desktop CPU.
More on GPU options for local LLMs: Best GPU for Local LLMs Under $400 in 2026.
Related guides
- Ollama vs llama.cpp: Qwen 2.5 14B on RTX 3060 12GB (2026)
- llama.cpp vs vLLM for Single-User Chat (2026)
- Is the MSI RTX 3060 12GB Still Worth It for 1080p in 2026?
- Best AM4 CPU to Pair With an RTX 3060 12GB
- Best GPU for Local LLMs Under $400 in 2026
Browse the full GPU catalog and home-lab hardware for related picks.
Frequently asked questions
Since Ollama uses llama.cpp under the hood, why compare them at all?
Ollama bundles a specific llama.cpp build with fixed defaults and its own model registry, so it trades tuning control for convenience. Running llama.cpp directly lets you compile with the latest CUDA flags, pick exact offload layers, and enable features like flash attention before Ollama exposes them. On the same RTX 3060 those choices can shift tok/s.
How much throughput does Ollama's wrapper cost?
For a single-user chat session the overhead is usually small — the heavy lifting is identical llama.cpp inference — but Ollama's conservative defaults and its keep-alive model management can leave a few percent on the table versus a hand-tuned llama.cpp invocation. Power users chasing every token favor raw llama.cpp; most users won't notice the gap.
Which is easier to keep updated with new models?
Ollama's registry makes pulling a new model a single command, and it handles templating and stop tokens for you. llama.cpp requires you to source GGUF files yourself and set chat templates manually, which is more work but gives you same-day access to brand-new quantizations before they appear in Ollama's library.
What flags matter most for an RTX 3060?
The key ones are -ngl to offload all model layers to the 12GB GPU, --flash-attn to cut KV-cache memory, and --kv-cache-type to quantize the cache so you fit more context. Getting -ngl high enough to keep the whole model on the GPU is the single biggest lever for tok/s on a 12GB card.
Do I need a fast SSD for either tool?
A quick NVMe such as the Samsung 970 EVO Plus shortens the load time when llama.cpp or Ollama reads multi-gigabyte GGUF weights into VRAM, but it does not change steady-state generation speed, which is GPU-bound. If you frequently switch models, faster storage noticeably improves the swap-in experience.
Citations and sources
- llama.cpp — ggml-org/llama.cpp on GitHub
- Ollama — ollama/ollama on GitHub
- RTX 3060 GPU specifications — TechPowerUp
This piece is editorial synthesis based on publicly available information. No independent first-party benchmarking is reported.
