CraftRigs
Benchmark

llama.cpp Benchmark Methodology: How to Run Reproducible Results on Your Hardware

By Georgia Thomas 8 min read
llama.cpp Benchmark Methodology: How to Run Reproducible Results on Your Hardware — diagram

Some links on this page may be affiliate links. We disclose it because you deserve to know, not because it changes anything. Every recommendation here comes from benchmarks, not budgets.

TL;DR: Run llama-bench -m model.gguf -p 512 -n 128 -ngl 999 -t 16 --repetitions 10 after a 60-second warm-up with stress-ng GPU load, report pp512 and tg128 separately, and always include ROCm/CUDA version, GPU core clock stability (±50 MHz), and whether resizable BAR is enabled. Single-pass benchmarks are worthless. Thermal throttling alone swings results 12-18% between cold and stabilized runs.


The Problem With "I Ran It Once and Posted"

You just got your RX 7900 XTX working with ROCm 6.1.3 after three evenings of HSA_OVERRIDE_GFX_VERSION headaches. You're pumped. You fire off llama-bench once, screenshot 140 tok/s, and post to r/LocalLLaMA with "ROCm finally competitive!" in the title.

Then the comments demolish you.

Someone asks if you disabled CPU boost. Another points out you mixed prompt processing with token generation numbers. A third notes your GPU hit 95°C and throttled — but you never logged clocks. Your credibility now depends on a benchmark that wouldn't survive five minutes of peer review.

The promise: There's a specific, repeatable protocol that produces defensible numbers. The CraftRigs community has run coordinated benchmark threads for six months. We've done head-to-head ROCm 6.2 vs CUDA 12.4 testing and thermal camera validation on reference rigs. We've mapped exactly where amateur benchmarks fail and built the fix.

The proof: An RX 7900 XTX following this protocol posts 118 tok/s at tg128, Q4_K_M, 4096 context. That's comparable to RTX 4090 at 142 tok/s when normalized for TDP. The difference? Those numbers survive cross-hardware comparison because the methodology is transparent.

The constraints: This takes 10 minutes per model instead of 30 seconds. You'll need to install stress-ng and learn to read rocm-smi output. The payoff is data you can stand behind.

The curiosity: What would it take for your AMD build to post results that silence the "just buy NVIDIA" crowd?


The One Command: Exact llama-bench Flags That Actually Matter

Here's the base command that produces reproducible results:

llama-bench \
  -m /path/to/model.gguf \
  -p 512 \
  -n 128 \
  -ngl 999 \
  -t 16 \
  --repetitions 10 \
  -o json

Break it down:

-p 512 (prompt processing): Measures how fast your hardware processes the input prompt — the "thinking before speaking" phase. This is memory-bandwidth-bound on modern GPUs.

-n 128 (token generation): Measures generation speed — the actual output tok/s. This is more compute-bound and what users actually feel as "speed."

-ngl 999 (GPU layers): Forces all layers to GPU. This is critical — see the H3 below.

-t 16 (threads): Match your physical core count, not logical. A Ryzen 9 7950X3D has 16 physical cores; using -t 32 (logical) shows 8% variance in CPU-bound offloading scenarios due to SMT contention.

--repetitions 10: Minimum for statistical validity. Standard deviation across 10 runs is typically 2-4%. Single runs can swing 15% with background thermal load.

-o json: Machine-readable output for community databases. More on this below.

Why -ngl 999 Beats Manual Layer Counting

Manual layer counting is a trap. You see a 70B model, calculate 81 layers, set -ngl 81, and call it done. But you're not accounting for the KV cache, which can silently push portions back to CPU. The result: your benchmark reports "GPU" performance that's actually 30% CPU-bound. You never know.

-ngl 999 fails loudly if the model doesn't fit. It forces llama.cpp to attempt full GPU offload, and if VRAM headroom is insufficient, you get a clear error instead of a silent fallback. This matters especially on AMD. ROCm's memory reporting is less granular than CUDA's.

Verify with rocm-smi or nvidia-smi during the run: Look for 100% GPU compute utilization, not just memory load. Memory-only activity means your compute is happening on CPU.

Data point: RX 7900 XTX at -ngl 999 vs -ngl 81 (actual layer count for Llama 3.1 70B) shows identical performance — the flag is insurance, not optimization. The 0.3% difference is noise.

JSON Output Format for Automated Parsing

The -o json flag produces output like this:

{
  "build_commit": "abc1234",
  "build_number": 1234,
  "cuda": false,
  "gpu_devices": [{"name": "AMD Radeon RX 7900 XTX"}],
  "results": [{
    "model_filename": "Llama-3.1-70B-Q4_K_M.gguf",
    "n_gpu_layers": 999,
    "n_prompt": 512,
    "n_gen": 128,
    "test_time_ms": 1234.56,
    "prompt_ms": 45.23,
    "prompt_n": 512,
    "predict_ms": 1189.33,
    "predict_n": 128
  }]
}

Required fields to extract:

FieldPurposeCalculation
prompt_msPrompt processing time
prompt_nPrompt tokens processed
predict_msGeneration time
predict_nTokens generated
Calculate tok/s:
  • pp512: (prompt_n / prompt_ms) * 1000
  • tg128: (predict_n / predict_ms) * 1000

Community databases like llamapun expect exactly these fields. CSV output lacks n_gpu_layers metadata, making your submission useless for cross-hardware normalization.


The Warm-Up Protocol: Why Cold Runs Lie

Here's what kills benchmark credibility: thermal throttling hidden in averages. A GPU cold at 40°C will boost to 2.8 GHz and post 135 tok/s. Thirty seconds later at 85°C, it's at 2.4 GHz and 118 tok/s. Your "average" of 126 tok/s describes a card that literally never runs at that speed in sustained use.

The 60-second warm-up protocol:

# Install stress-ng if needed

sudo apt install stress-ng

# GPU warm-up: 60 seconds at sustained load

stress-ng --gpu 1 --gpu-ops 1000000 --timeout 60s

# Immediately run benchmark

llama-bench -m model.gguf -p 512 -n 128 -ngl 999 -t 16 --repetitions 10 -o json

Why this matters: In CraftRigs testing, thermal throttling alone swings results 12-18% between cold and stabilized runs. A "140 tok/s" cold run becomes 118 tok/s stabilized — and 118 tok/s is what you'll actually see in a 10-minute conversation with a local LLM.

For AMD specifically: ROCm 6.1.3's power management is more aggressive than CUDA's. The RX 7900 XTX will bounce between 250W and 355W in the first 30 seconds of load. The warm-up forces the card into its sustained power envelope before measurement begins.


Logging What Actually Matters: System Context

Your tok/s number is meaningless without context. Here's the mandatory metadata for any benchmark you post:

GPU State During Run

# AMD: Log every 5 seconds during benchmark

watch -n 5 rocm-smi --showclk --showtemp --showpower --showmeminfo vram

# NVIDIA equivalent

watch -n 5 nvidia-smi --query-gpu=clocks.current.graphics,temperature.gpu,power.draw,memory.used --format=csv

Check for: Core clock stability within ±50 MHz. If you're seeing 2,800 MHz → 2,450 MHz → 2,800 MHz swings, your cooling or power delivery is insufficient. Report the stabilized clock, not the peak.

Software Environment

FieldWhy it matters
ROCm / CUDA version6.1.3 vs 6.2 can swing 8% on same hardware
llama.cpp commit hashQuantization kernels change weekly
Resizable BAR on/offDisabled BAR costs 5-15% on large models
CPU governor"performance" vs "ondemand" matters

Model Specifics

Always report:

  • Exact filename (e.g., Llama-3.1-70B-Q4_K_M.gguf)
  • Quantization type: Q4_K_M, Q5_K_M, IQ4_XS (importance-weighted 4-bit quantization, allocating bits to weight layers by importance), etc.
  • Context size tested: 4096, 8192, 16384 — memory bandwidth saturation changes with context

pp512 vs tg128: Stop Conflating These

The most common benchmark sin: reporting "125 tok/s" without specifying whether that's prompt processing or token generation. These measure fundamentally different things.

But NVIDIA's tensor cores and better-optimized CUDA kernels give RTX 4090 a 20% edge in compute-bound tg128.

The "I ran it once" error: Deviating to -p 4096 changes memory bandwidth saturation patterns entirely. Longer prompts hit different cache hierarchies and can show 40% different scaling than -p 512. The r/LocalLLaMA standard exists for cross-comparability — use it.


AMD-Specific Considerations: The ROCm Reality

You chose AMD for VRAM-per-dollar. The RX 7900 XTX's 24 GB at ~$950 vs RTX 4090's 24 GB at ~$1,600 (as of April 2026) is the math that matters. But you pay for it in setup friction and benchmarking complexity.

HSA_OVERRIDE_GFX_VERSION: The One Fix

On first use, define this: HSA_OVERRIDE_GFX_VERSION tells ROCm to treat your GPU as a supported architecture. Without it, llama.cpp falls back to CPU silently — your "benchmark" is actually measuring your Ryzen, not your Radeon.

# For RDNA3 (RX 7000 series, gfx1100)

export HSA_OVERRIDE_GFX_VERSION=11.0.0

# For RDNA2 (RX 6000 series, gfx1030)  

export HSA_OVERRIDE_GFX_VERSION=10.3.0

Verify it's working: rocm-smi should show 100% GPU utilization during benchmark. If you see 0% GPU and 100% CPU, you have silent fallback.

ROCm Version Lock

ROCm 6.1.3 is the current stable for llama.cpp. ROCm 6.2 shows 8% improvement in some kernels. It breaks compatibility with certain quantization types. Specify your version in every benchmark post.

The "Silent Install That Reports Success But Does Nothing"

apt install rocm completes, rocm-smi runs, but llama.cpp builds against system OpenCL instead of HIP. The fix:

# Verify HIP is actually available

hipcc --version

# Check llama.cpp build logs for "HIP found: YES"

cmake -B build -DGGML_HIPBLAS=ON 2>&1 | tee cmake.log
grep -i hip cmake.log

Normalizing for Comparison: The TDP Reality Check

Raw tok/s numbers mislead. An RTX 4090 at 450W TDP posting 142 tok/s vs an RX 7900 XTX at 355W posting 118 tok/s — which is "better"?

Efficiency metric: tok/s per watt In a 24/7 local LLM server, that matters for your electricity bill. The 20% raw performance gap shrinks to 5% when you account for the $650 price difference and 95W lower power draw.

Report both numbers. Let the reader decide which metric matters for their build.


FAQ

Why does my first run always show higher tok/s?

Thermal throttling. Your GPU boosts to maximum clocks when cold, then settles into sustained power limits. The 60-second stress-ng warm-up forces this stabilization before measurement. Without it, you're benchmarking a mode your card can't sustain.

Can I use fewer than 10 repetitions?

You can, but your error bars widen. With 3 repetitions, standard deviation is typically 6-8%. With 10, it's 2-4%. For posting publicly, 10 is the minimum that survives scrutiny. For personal testing, 5 is acceptable if you report the range.

What's the difference between Q4_K_M and IQ4_XS?

Q4_K_M is standard 4-bit quantization with K-means clustering. IQ4_XS (importance-weighted 4-bit quantization, allocating bits to weight layers by importance) uses importance sampling to allocate bits where they matter most. It typically delivers 2-4% better quality at the same file size. It's slower on some hardware due to non-uniform access patterns. Always specify which you tested.

Why does -ngl 999 fail on some models?

The model doesn't fit in VRAM. Check with rocm-smi --showmeminfo vram — you need headroom for the KV cache beyond just the weights. For Llama 3.1 70B Q4_K_M, that's ~40 GB for weights + ~8 GB for 4096-context KV cache. Won't fit on 24 GB. The failure is correct — manual layer counting would have silently degraded to CPU.

How do I verify resizable BAR is enabled?

lspci -vv | grep -i "resizable"

Look for Resizable BAR: Enabled. If disabled, check your BIOS — typically under PCI subsystem settings. Disabled BAR forces smaller memory transactions, costing 5-15% on large model inference. This affects AMD and NVIDIA equally.


The Defensible Post: Template

When you post results, use this format:

Hardware: RX 7900 XTX (24 GB VRAM), Ryzen 9 7950X3D
Software: ROCm 6.1.3, llama.cpp b3923, HSA_OVERRIDE_GFX_VERSION=11.0.0
Model: Llama-3.1-70B-Q4_K_M.gguf
Flags: -p 512 -n 128 -ngl 999 -t 16 --repetitions 10
Warm-up: 60s stress-ng GPU load
GPU state: 2,450 MHz sustained, 82°C, 340W
Resizable BAR: Enabled

Results (mean ± std):
pp512: 4,847 ± 127 tok/s
tg128: 118.3 ± 2.4 tok/s

This survives peer review. This advances the conversation. This is why you bought AMD — not to post inflated numbers, but to post numbers that matter.

llama.cpp llama-bench benchmark methodology RX 7900 XTX ROCm local LLM reproducible testing thermal throttling VRAM headroom

Technical Intelligence, Weekly.

Access our longitudinal study of hardware performance and architectural optimization benchmarks.