holistechlabsllama.cpp ↔ halogen
DEEN

Measured · 8 September 2026 · one pass per engine

Qwen3.8-Flash-Next on the HP ZBook Ultra G1a

llama.cpp (rocmfpx-Fork) against halogen-flash-server 0.4.4

Authors

Dipl.-Ing. Sören GebbertInstitut für holistische Technologieforschung GmbH Claude Code Opus 5Anthropic

Machine

HP ZBook Ultra G1a

SoC
Ryzen AI Max+ PRO 395
GPU
Radeon 8060S, gfx1151, 70 W budget
Memory
128 GiB LPDDR5X-8000, soldered – shared by CPU and GPU as unified memory
OS
Ubuntu 24.04, 140 W power supply, platform_profile=performance

Model – the same in both runs

Qwen3.8-Flash-Next

Size
about 180B – measured at 176.9 billion parameters
Build
“512×56B”, 48 blocks, 512 experts, 10 of them active per token
Context
up to 262,144 tokens, vocabulary 248,320
Tasks
20: 12 × AIME 2024/2025, 4 × code, 4 × context (~69,000 tokens)

Engine A

llama.cpp · rocmfpx-Fork

Version
commit e302744, Vulkan/RADV
Weights
Q4_0_ROCMFP4_STRIX_LEAN (GGUF), 4.5 bpw
Flags
-c 262144 -fa on -ngl 999 -np 1 --cache-ram 0
Budget
65,536 tokens

Engine B

halogen-flash-server 0.4.4

Version
image halogen-flash-server:0.4.4, Podman
Weights
w4b.hgn + quality overlay, 5.53 bpw
Flags
HALOGEN_CTX=134400, 4 slots, HALOGEN_PROMPT_CACHE=0
Budget
49,152 tokens (also the prefill arena)

halogen on this machine

886tokens/s

Prompt processing on a 69,000-token context

39.9tokens/s

Output on long context the same 69,000 tokens ahead of it

39.3tokens/s

Output in chat short questions, under 800 prompt tokens

157minutes

for all twenty tasks 339,134 tokens generated

Means over the tasks in each group, measured on the run's own answers – not on filler prompts. Temperature 0, prompt cache off, 70 W GPU budget.

01 · Setup and memory

Why a 180B model runs on a notebook at all

A model of 176.9 billion parameters on a machine with 128 GiB of memory (125.1 GiB of it usable), whose dedicated graphics memory amounts to 0.5 GiB – that only works because three things come together: how the model is built, how the platform handles memory, and what the engine makes of both.

1 · The model

Sparse, not dense

total
176.9 billion parameters, 48 blocks
active
10 of 512 experts per token
Effect
Each token is computed through a fraction of the model – but all of it still has to be held in memory

2 · The platform

One memory for both

VRAM
only 0.5 GiB dedicated
GTT
110 GiB of system memory opened up to the GPU (amdgpu.gttsize)
Effect
The GPU computes on the same LPDDR5X as the CPU – no detour over PCIe, no copy

3 · The engine

What halogen makes of it

load
124.1 GB checkpoint mapped, 65.64 GiB of it pinned – in 3.6 s at 19.4 GB/s
KV
one pool of 134,400 positions = 3.7 GiB, about 28 KiB per position
ready
after 15.1 s, with 81.1 GiB of memory left free

Why memory is the first question

With a model this size, memory decides before anything else does. It is the hard limit: what does not fit does not run. And what barely fits takes up so much of the machine for the duration of the computation that nothing else has room beside it – on a notebook that doubles as a work machine, that is the difference between usable and blocked.

Two figures therefore stand side by side here: how much memory was tied up during operation, and in what form it was held. For these two engines the answers diverge, and the second one determines how the first should be read.

What operation ties up

A sensor recorded the memory state every two seconds throughout both runs. Two terms in the table below need a word of explanation.

GTT stands for Graphics Translation Table and denotes the part of system memory that the graphics driver makes reachable for the GPU: pages of system memory are mapped into the GPU's address space so that it can read and compute there without copying anything into dedicated graphics memory. On this platform that is the rule rather than the exception – only 0.5 GiB are dedicated, and the ceiling for the GTT is set by the kernel parameter amdgpu.gttsize, here 110 GiB. What the table reports as GTT is therefore not additional memory, but the share of that same system memory handed to the GPU at the time of measurement.

MemAvailable is the kernel's estimate of how much memory a new application could still obtain – including whatever it could free up to satisfy the request. The row “left free” gives the low-water mark of that value across the run, that is, what remained for the rest of the system at the worst moment.

Memory llama.cpp halogen
On disk 98.48 GiB
3 GGUF shards
115.58 GiB
+ 2.31 GiB overlay
GTT peak during the run 78.7 GiB 52.7 GiB
GTT, median 77.5 GiB 41.0 GiB
held by the system as allocated 88.4 GiB 56.6 GiB
left free, low-water mark 36.7 GiB 68.5 GiB

5,065 samples across the complete halogen run. For llama.cpp there are 5,854 samples, but they cover only the last three tasks: that run was carried out in two sessions and the sensor ran during the second. Those three are the context tasks – the most memory-hungry part of the set.

Memory over the course of the runs Sampled every two seconds. Area: held by the system as allocated. Line: the share handed to the GPU (GTT). HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 0 25 50 75 100 125 Memory in GiB 0 30 60 90 120 150 llama.cpp allocated GTT Minutes since the recording started The recording covers only the last three tasks - the three context tasks, the most memory-hungry part of the set. 0 30 60 90 120 150 halogen allocated GTT Minutes since the recording started Complete run, from engine start to the last task. Total system memory: 125.1 GiB
The whole measurement, over time. The filled area is the memory the system holds as allocated, the grey line the share of it handed to the GPU. Both panels share one scale.

Two ways of holding the same weights

The bottom two rows invite being read as a statement of requirement. They would be that only if both engines claimed memory in the same way. They do not:

  • llama.cpp places the weights as GTT allocations with -ngl 999 – device memory drawn from that same system RAM. Such pages cannot be reclaimed; they reduce MemAvailable by their full size.
  • halogen maps the checkpoint instead (mapped 124.1 GB). Its pages are clean and file-backed. The kernel counts them as available, because it can drop them at any time and read them back from disk when needed.

The engine's own log shows this plainly: pinned 65.64 GiB is followed a few lines later by host memory left for everything else: 81.1 GiB. The pinned pages do not show up as used memory at all.

What the table reports, then, is how much memory the system holds as allocated. That is the figure that matters in practice – it decides whether the machine can still be worked on alongside the model. It is not a statement of requirement in the sense of “the engine needs this much”, and comparing the two columns measures the kernel's bookkeeping as much as the engines' appetite.

What the SSD is for

Of the 115.6 GiB that are mapped, 65.64 GiB are pinned; the remainder stays as file pages and can be evicted. That makes the drive part of the memory path: it carries the cold start and catches whatever is pushed out under memory pressure. Only this fallback allows a checkpoint of this size onto a machine with 128 GiB at all – and it turns the choice of a fast SSD into a decision about start-up time, not just about disk space.

The start-up itself shows how large that difference is. With the checkpoint still in the page cache, the 65.64 GiB are pinned in 3.6 seconds – 19.4 GB/s, which is to say no disk access at all. Without it, the engine really does read from the drive: 2.1 GB/s, and 3.6 seconds become 33.2. Both figures come from the same machine and the same checkpoint; all that differs is the path the data takes.

How much was paged back in during the runs is not something this measurement shows: the sensor recorded GPU power, clock, temperature, GTT and MemAvailable, but no I/O counters. A dependable answer would require a separate run with block-layer statistics.

The real limit is the generation budget

The measurement ran with a context window of 134,400 positions; the model supports 262,144. That full window is workable on this machine too. KV memory costs about 28 KiB per position – the positions used take 3.7 GiB, the full 262,144 accordingly 7.2 GiB. With a pool of that size and the same generation budget of 49,152 tokens, the engine on the otherwise idle machine is ready after 18.9 seconds and answers the longest prompt in the task set, 68,801 tokens, at 1,005 tokens/s of prompt processing, leaving 76.3 GiB of memory free.

The binding constraint is a different one: HALOGEN_MAX_TOK not only caps how long an answer may become, it also sizes the prefill arena.

HALOGEN_MAX_TOK Prefill arena contiguous 2 MiB blocks left Prefill of 60 tokens
32,768 16.7 GiB
49,152 used ~25 GiB 2,152 (4.3 GiB) 2.3 s
65,536 33.4 GiB 184 (368 MiB)127 s

The bottleneck is not the amount of memory but its shape. The engine needs large contiguous blocks; when only small ones remain, it spends the start-up compacting. At a budget of 65,536 tokens, 184 contiguous 2 MiB blocks are left after start-up, after 637 compaction passes for the pool reservation alone, and a prefill of 60 tokens then takes 127 seconds instead of 2.3. The large KV pool pushes in the same direction, though less hard: with 262,144 positions, 275 blocks remain after 285 compaction passes.

For the measurement the choice therefore fell on 134,400 positions: that window covers the longest prompt in the set together with the full budget, and leaves the arena the most contiguous memory. Anyone who needs the full context can have it – and pays for it with a tighter start.

In context

Prefilling in pieces of 32,768 tokens, the pinning and the shared KV pool are properties of this build of the engine, not of the model. The weights of the two runs are not the same (4.5 against 5.53 bpw): halogen's checkpoint is both the more precise and the larger one. And llama.cpp ran with n_ctx 262,144 against halogen's 134,400 – without effect on the results, since no request goes beyond that, but part of the memory difference traces back to it. The full set of caveats is in chapter 11.

02 · And in comparison

Same machine, same tasks, the other engine

Success rate
17 : 18 of 20
Exact McNemar p = 1.000 – no measurable difference in capability.
Prompt processing
7.1 ×
126 against 886 tokens/s on a 69,000-token prompt.
Length of the run
486 157 min
The same twenty tasks, one after another, with nothing else running.
Reproducibility
0/18 : 9/9
Byte-identical answer pairs to the same question at temperature 0.

What follows below is this measurement in nine chapters: what was asked, what each of them solved, where the time goes, how they approach the tasks – and what the work does not show.

03 · What was measured

Twenty tasks, three kinds, two stacks

Twelve tasks from the AIME competitions of 2024 and 2025 – integer answers, checkable by machine. Four code tasks of our own, whose answers are executed against test cases in a rootless sandbox with no network. And four context tasks of our own over a measurement log of some 69,000 tokens that the prompt carries in full.

The self-made tasks are the contamination-free control on the public AIME sets: they were nowhere on the web when the model was trained. Both engines received exactly the same prompts in the same order, at temperature 0, with the prompt cache off, on the 140 W power supply in the performance profile. Nothing else ran on the machine during a run; the runner aborts if it finds a foreign process or a different power profile.

The 20 tasks Where they come from, what they are about, how long the prompt was and who solved them. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen Number = prompt tokens · filled = solved, open = not solved AIME 2024 public competition aime2024-1 System of logarithms, three unknowns 287 aime2024-2 Intersections of nested absolute values 219 aime2024-3 Complex numbers, largest real part 186 aime2024-4 Number theory: smallest p with p² | n⁴+1 203 aime2024-5 Two-digit numbers in base b 261 aime2024-6 Circle geometry, tangents, power of a point 492 AIME 2025 public competition aime2025-1 Divisibility in base-b notation 169 aime2025-2 Isosceles trapezoid with an incircle 222 aime2025-3 Sawtooth function meets a parabola 335 aime2025-4 Six points on a straight line 237 aime2025-5 Two circles, internally tangent, rectangle 934 aime2025-6 Regular 24-gon, matchings of equal length 194 Own coding tasks run in a sandbox against test cases c1 Damerau-Levenshtein distance, unrestricted 222 c3 Maximum flow in a directed network 211 c4 Lexicographically smallest topological sort 201 c6 Subsets with a target sum, negatives allowed 188 Own long-context tasks a log of 1,400 measurement rows k1 Snow depths at Ostgrat below 0 °C 68,824 k2 Highest gust with snow above 300 cm 68,832 k3 Rows meeting three conditions 68,845 k4 Two differences from two rows 68,851 Both AIME sets are public and are probably part of the training data. For comparing two quantisations of the same model that is acceptable — both carry the same memory. The eight own tasks are the contamination-free control.
The task set. Six AIME 2024, six AIME 2025, four code tasks, four context tasks. The number beside each task is the length of its prompt in tokens – the four context tasks are the only ones that take the long road.
TaskTopic Source Prompt tokens llama.cppSeconds halogenSeconds
c1Damerau-Levenshtein distance, unrestrictedCode222solved2,289solved1,108
c3Maximum flow in a directed networkCode211solved1,230solved631
c4Lexicographically smallest topological sortCode201solved70solved37
c6Subsets with a target sum, negatives allowedCode188solved2,812solved962
k1Snow depths at Ostgrat below 0 °CContext68,824wrong1,832solved893
k2Highest gust with snow above 300 cmContext68,832solved2,284solved495
k3Rows meeting three conditionsContext68,845budget spent6,394solved822
k4Two differences from two rowsContext68,851solved582solved84
aime2024-1System of logarithms, three unknownsAIME2024287solved76solved37
aime2024-2Intersections of nested absolute valuesAIME2024219budget spent4,905budget spent1,302
aime2024-3Complex numbers, largest real partAIME2024186solved69solved30
aime2024-4Number theory: smallest p with p² | n⁴+1AIME2024203solved897solved301
aime2024-5Two-digit numbers in base bAIME2024261solved310solved339
aime2024-6Circle geometry, tangents, power of a pointAIME2024492solved2,717solved976
aime2025-1Divisibility in base-b notationAIME2025169solved35solved22
aime2025-2Isosceles trapezoid with an incircleAIME2025222solved65solved57
aime2025-3Sawtooth function meets a parabolaAIME2025335solved913loop763
aime2025-4Six points on a straight lineAIME2025237solved68solved49
aime2025-5Two circles, internally tangent, rectangleAIME2025934solved367solved257
aime2025-6Regular 24-gon, matchings of equal lengthAIME2025194solved1,232solved267

Click a column header to sort

One difference in the setup is worth naming, because it would otherwise look like an advantage: llama.cpp ran with a token budget of 65,536, halogen with 49,152 – for halogen that value is also the prefill arena and would have claimed some 33 GiB at 65,536. Scoring is therefore done on the shared budget of 49,152. That conversion costs llama.cpp one solved task and hands neither engine anything.

04 · Capability

In capability, nothing separates them

17 against 18 tasks solved out of twenty. Evaluated as paired data: exact McNemar, p = 1.000. With twenty tasks and 3 answered differently, a real difference of this size cannot be told apart from chance.

Choosing either engine because it gives better answers means choosing on grounds this measurement does not provide. That is not a weakness of the comparison but its most important result: it settles the question of capability and clears the way for the question that does show differences.

Same hit rate, a third of the time Left: tasks solved out of 20, paired analysis. Right: what one solved task cost in time. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen Tasks solved llama.cpp 17 / 20 halogen 18 / 20 Exact McNemar test: p = 1.000 The difference is indistinguishable from chance. 19 of 20 tasks: the same solution path Judged blind, both paths read in full. Seconds per solved task llama.cpp 1,714 s 17 tasks solved in 486 minutes halogen 524 s 18 tasks solved in 157 minutes 3.3× more time per result at the same hit rate.
Same success rate, different bill. On the left what was solved, on the right what it cost. The left half is the reason the right half is interesting at all.

05 · Prompt processing

The lead grows with the length of the prompt

On short questions llama.cpp reads the prompt at 99 tokens/s and halogen at 129 – noticeable, but not dramatic. On the four context tasks of nearly 69,000 tokens it is 126 against 886 tokens/s: a factor of 7.1 in halogen's favour.

Prompt processing, in bars Left the speed, right what it amounts to: the wait until the first token appears. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen Input tokens read per second short questions, under 800 tokens 99 t/s 129 t/s long context, 69,000 tokens 126 t/s 886 t/s Wait for the first token at a 69,000-token prompt, mean of k1 to k4 llama.cpp 9:08 min halogen 1:18 min Nine minutes of waiting become just over one. Per task, across the four tasks of this kind.
Short against long. One quantity, measured twice: on short questions and on the context tasks. llama.cpp stays where it is; halogen pulls away exactly where the prompt gets expensive.

Why this matters is not in the number but in the way of working: a long prompt is the normal case as soon as a model is asked to read a file, a log or a conversation. Right there, llama.cpp takes a good seven minutes for what costs halogen one – before the first character of the answer appears.

Prompt processing: 7× faster where it actually hurts Input tokens read per second, against prompt length. Same 20 tasks, same machine, cache off. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen 0 200 400 600 800 1,000 200 500 1k 2k 5k 10k 20k 50k 100k Tokens per second Prompt length in tokens (logarithmic) no measurements in this range 886 t/s 126 t/s 7.1× at a 69,000-token prompt short questions: 1.1× to 1.9×
The same measurement as a curve. Each point is one task of the run, the axis logarithmic. The gap between the two lines is the lead – and it opens towards the right.

06 · Output speed

One collapses on long context, the other does not

On short questions llama.cpp produces 21.1 tokens/s and halogen 39.3 – a good 1.9 × faster. With 69,000 tokens ahead of it, llama.cpp drops to 9.7 tokens/s, roughly half its own speed. halogen stays at 39.9.

Output speed: 40 tokens/s against 10 on long context Generated tokens per second. With 69,000 tokens of context llama.cpp loses half its speed; halogen loses none. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen short questions under 800 prompt tokens 21.1 t/s 39.3 t/s long context 69,000 prompt tokens 9.7 t/s 39.9 t/s all 20 tasks average across the whole run 18.8 t/s 38.7 t/s Means over the tasks in each group, measured on the run's own answers — not on filler prompts.
What context costs. The remarkable part is not the distance between the colours but the drop within the blue: llama.cpp loses half its speed, halogen none.

The two together – slower to read in, then slower to write out – are why the run times differ in the end by a factor rather than by percentages.

07 · Why this counts

The difference is throughput, not verbosity

A faster run could simply be a shorter one. It is not: over the twenty tasks llama.cpp generated 362,635 tokens, halogen 339,134 – that is 6.5 % fewer for halogen.

Both therefore think for a comparable length about the same tasks. The difference in time does not come from one of them being more concise, but from how fast the same amount of text moves through the machine.

The same amount of thinking, a third of the time Both engines produce almost the same amount of text. The difference in time is throughput, not verbosity. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen Tokens generated across all 20 tasks 362,635 339,134 Total running time from the start to the last answer 486 min 157 min Tokens per second across the whole run, waiting included 12.4 t/s 36.0 t/s 0.94× the text, but in 0.32× the time — the difference is throughput, not the length of the answers.
Almost the same amount of text. If the bars differed markedly, part of the time saved would be mere terseness. They do not.

08 · Taken together

Seconds per solved task

Success rate and speed brought into one figure: 1,714 against 524 seconds per solved task. A factor of 3.3 – at the same success rate. Across the whole run: 486 against 157 minutes.

Every single task, every single wait Seconds from request to finished answer, logarithmic. Open circle: task not solved. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen 30 s 1 min 2 min 5 min 10 min 20 min 50 min 100 min k3 aime2024-2 c6 aime2024-6 c1 k2 k1 aime2025-6 c3 aime2025-3 aime2024-4 k4 aime2025-5 aime2024-5 aime2024-1 c4 aime2024-3 aime2025-4 aime2025-2 aime2025-1 halogen was faster on 19 of 20 tasks. The only exception: aime2024-5.
Every task on its own. No average hides an outlier here: the lead does not rest on a few tasks but runs through the whole set.

09 · The blind control

They do not just solve as often, they solve alike

A judge who did not know which engine had produced which text was given both complete solutions for each of the twenty tasks. The result: the same approach in 19 of 20 cases – the same substitution, the same theorem, the same algorithm.

Checking behaviour does not differ either: who recomputes, who verifies, who walks into a dead end and how they find their way out is distributed alike across both stacks. That supports the finding of chapter 04 from another side – not through the success rate but through the road to it.

Not just the same score — the same reasoning A blind judge read both complete solution paths for every task, randomly swapped, with no project context. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen 19 of 20 tasks: the same approach Same substitution, same theorem, same algorithm. aime2024-1 aime2024-2 aime2024-3 aime2024-4 aime2024-5 aime2024-6 aime2025-1 aime2025-2 aime2025-3 aime2025-4 aime2025-5 aime2025-6 c1 c3 c4 c6 k1 k2 k3 k4 Only deviation: aime2025-4 — coordinates versus segment algebra, both paths correct. Where it went wrong Never the understanding of the task, never an arithmetic slip. llama.cpp: 1× method, 1× gave out before the end halogen: 2× gave out before the end How they checked their work worked through examples 7 6 second, different route 6 6 same calculation again 4 3 cross-check 2 2 not at all 1 3 Dealing with dead ends no dead end 14 12 backed out, started over 3 2 kept going 1 3 went in circles 2 3 Same paths, same checking effort — and still 3.3× faster per solved task.
What the blind judge saw. Same approach, same checking behaviour, same handling of dead ends. The judge ran in an empty working directory with no project context and never knew which was which.

10 · The unplanned finding

Same question, same answer – or else not

At temperature 0 the same request ought to yield the same answer. llama.cpp does so in not a single case: 0 of 18 answer pairs across 18 runs in two configurations were byte-identical. halogen in all of them: 9 of 9, across restarts of the engine.

On one task the answers to the same question ranged between 1,165 and 36,455 tokens. The obvious suspect – llama.cpp carrying the slot state over between requests – was ruled out by a second pass with cache_prompt: false. Only the first request after a server start matched, and that one byte for byte across two independent starts: the variance sits in the state carried between requests, not in sampling.

The same question, asked three times Seconds per run, identical request at temperature 0, cache off. HP ZBook Ultra G1a · Ryzen AI Max+ PRO 395 · Qwen3.8-Flash-Next llama.cpp: rocmfpx fork, Vulkan · halogen: halogen-flash-server 0.4.4 · temperature 0, prompt cache off · measured 8 September 2026 llama.cpp halogen 0 200 400 600 800 1,000 1,200 Seconds per run aime2024-1 102 68 58 2,559 / 1,866 / 1,625 tokens 38 40 40 3 × 1,609 tokens · identical c4 98 42 52 2,502 / 1,165 / 1,436 tokens 36 38 38 3 × 1,365 tokens · identical aime2024-4 788 1,143 680 14,315 / 19,640 / 12,532 tokens 301 324 322 3 × 12,076 tokens · identical Cache off, cache_prompt: false, same server, same parameters. The final answer was correct in all 18 runs — only the path to it was not repeatable.
Three runs per task, compared byte for byte. The final answers were correct every time. What is not reproducible is the path and its price – not the result.

In practice this means two things. First: anyone who has to document an answer or bisect a regression needs reproducibility – and gets it here from only one of the two. Second, and less comfortable: the llama.cpp times in this measurement are a sample, not a constant. They should be read with a wide spread that is not quantified here.

11 · What this work does not show

Four limits to read along with it

The most important caveat

These are not the same weights. llama.cpp ran at 4.5 bpw (Q4_0_ROCMFP4_STRIX_LEAN, GGUF), halogen at 5.53 bpw (.hgn w4b plus a quality overlay: 12 tensors in q8g64, 2.31 GiB). What was compared is the stack of engine and quantisation, not the engine alone.

  • Contamination. AIME 2024 and 2025 are public and are probably in the training data. For comparing two quantisations of the same model that is tolerable – both share the same memory of them – but it is no basis for an absolute claim about capability. The self-made code and context tasks are the control.
  • A notebook with a 70 W GPU budget is not a desktop. The absolute figures hold for this machine on this power supply.
  • One pass per engine – and for llama.cpp that is demonstrably a sample, not a constant (chapter 10).
  • Today's state is not the state that was measured. The rocmfpx fork has moved thirteen commits on since the day of the measurement, with an optimisation aimed at exactly the weak spot measured here; halogen stands at 0.5.0. Neither was re-measured – this page holds for the versions it names.