Methodology

Every number on this site is produced by one harness — about 350 lines of dependency-free Python — and it is the same file on every machine. No per-machine tweaks, because a benchmark you edit per machine is not a benchmark.

What is measured

The harness talks to an OpenAI-compatible /v1/chat/completions endpoint and times the response stream from the client side. Measuring client-side is deliberate: llama.cpp, ollama, vLLM and MLX all report internal statistics differently, and some do not report them at all. Timing the stream is the only method that produces numbers comparable across all of them.

How runs are conducted

The four scenarios

Scenario In Out What it exposes
chat_short 128 256 Interactive feel; latency-dominated
chat_long_context 8192 256 RAG and long-document work
code_generation 512 1024 Agentic coding; decode-dominated
summarization 16384 128 Worst case for weak prefill

Scenarios are frozen. Adding one is fine; editing an existing one would silently invalidate every previously published comparison, so it does not happen.

How quality is scored

Speed is only half the question. A model that answers twice as fast and gets it wrong is not faster at anything useful. Quality is measured with a second harness against MMLU-Pro, a public set of 12,032 graduate-level multiple-choice questions spanning fourteen subjects, with up to ten options each rather than the usual four.

No model grades another model here, and I do not grade them either. That is worth stating plainly, because LLM-as-judge scoring has become common enough that readers reasonably assume it. Every question in MMLU-Pro ships with its correct answer as a field in the dataset. Scoring is a string comparison against that answer sheet.

Concretely, one real question and how it is scored:

Question 2804  (biology)
  Which of the following would most likely provide
  examples of mitotic cell divisions?

    A. cross section of muscle tissue
    B. longitudinal section of a shoot tip
    C. longitudinal section of a leaf vein
    ...                      (eight options in this case)

Answer sheet     "answer": "B"

Model writes     "...the shoot tip contains meristematic
                  tissue, which is actively dividing, so
                  the answer is B."

Extracted        B
Scored           correct

The extraction step is a regular expression, not a judgement call:

(?i)(?:answer|correct choice|correct option|best answer)\s*(?:is|:)\s*\**\s*\(?([A-J])\)?

It takes the last match in the response, not the first. That detail matters for reasoning models: one that weighs option B in the middle of its working and concludes D is scored on D, which is what a human marker would do.

What this scoring gets wrong

Results land in data/subject-sweep/ as JSON, one file per model, written incrementally after every subject so an interrupted run keeps everything measured up to that point. As with the speed numbers, the tables are rendered from those files at build time.

What the speed numbers are not

Single-stream benchmarks. They describe one person using one model interactively — the actual local-LLM use case — and they are a floor rather than a ceiling for anything serving more than one request at a time. That gap is now measured rather than guessed at: on this machine eight simultaneous requests produce 2.9× the total tokens per second, while each individual request runs at 36% of its solo speed. Read the published figures as the single-user number and that post for the batch one. They also reflect specific driver, backend and model versions, all recorded in each result file. A rerun six months later on the same hardware can legitimately produce different numbers, which is why the backend version is published alongside every result.

Reproducing them

# On the machine under test, with a server already running:
python3 bench/runner.py \
  --url http://localhost:11434 \
  --model llama3.1:8b \
  --label my-machine \
  --quant Q4_K_M

The result lands in data/runs/ as JSON. Those files are the primary record — the tables on this site are rendered from them at build time, so a published number and its source file cannot disagree, and no figure is ever retyped by hand.

The repository is currently private, so that command is not something you can run today. The description above is deliberately complete enough to reimplement: client-side stream timing against an OpenAI-compatible endpoint, one discarded warmup, five measured repetitions, medians with standard deviation, greedy decoding, and the four fixed prompt sizes in the table. If you rebuild it and get materially different numbers on comparable hardware, I would genuinely like to know.