← TinyGPT · docs · devlog · roadmap · speedup
source: docs/benchmark_first_run.md · view on GitHub ↗

First Worked Run — tinygpt bench

Captured 2026-05-30. Validates the harness wired up end-to-end against the existing in-process MLX inference path. This is the “smoke test” that proves the scaffold produces real numbers; it is not a publishable benchmark (n=5, no energy metrics, tiny model).

Design doc: docs/benchmark_harness_design.md.

What I ran

A 9.6 M-parameter byte-level transformer trained in the browser (shakespeare.bin, the canonical gallery checkpoint), driven through the new harness in single-stream mode. No powermetrics (the laptop isn’t running with sudo, so energy/ANE are skipped — graceful degradation is part of the design).

# from native-mac/
swift build -c release

# copy MLX kernel library next to the binary so MLX can find it via
# load_colocated_library() — see "MLX metallib lookup" caveat below.
cp /opt/homebrew/lib/mlx.metallib .build/release/mlx.metallib

./.build/release/tinygpt bench \
  --model ../browser/public/gallery/shakespeare.bin \
  --mode single \
  --prompt-tokens 64 \
  --gen-tokens 100 \
  --n-runs 5 \
  --warm-runs 1 \
  --no-energy \
  --output /tmp/tinygpt_bench_shakespeare.json

Markdown table the harness printed

tinygpt bench — tinygpt
---------------------------------
model:           ../browser/public/gallery/shakespeare.bin
workload:        single, prompt=64 tok, gen=100 tok, batch=1
runs:            5 (+1 warm)
energy metrics:  off
loaded — 9,608,704 params
prompt: 64 bytes/tokens (byte-level)

running…
done in 1.8s across 5 runs
metricmedianp95p99n
TTFT (ms)1.912.082.085
decode tok/s794.91835.06835.065
prefill tok/s33489.5539190.5239190.525
ITL (ms)1.151.962.84490
peak RSS (MB)257.5258.3258.35
energy/token (J)(skip — no sudo for powermetrics)

Warnings the harness flagged on this run:

Both of these are the harness behaving correctly. They are the guardrails the design doc §1 promised.

JSON shape (excerpt — metrics block from the same run)

{
  "engine": { "name": "tinygpt", "commit": "bc4d4a0" },
  "git_commit": "bc4d4a0",
  "git_dirty": true,
  "harness_version": "0.1.0",
  "metrics": {
    "ttft_ms":      { "median": 1.91, "p95": 2.08, "p99": 2.08, "n": 5 },
    "decode_tps":   { "median": 794.91, "p95": 835.06, "p99": 835.06, "n": 5 },
    "prefill_tps":  { "median": 33489.55, "p95": 39190.52, "p99": 39190.52, "n": 5 },
    "itl_ms":       { "median": 1.15, "p95": 1.96, "p99": 2.84, "n": 490 },
    "peak_rss_mb":  { "median": 257.46, "p95": 258.31, "p99": 258.31, "n": 5 },
    "energy_per_token_j": { "median": null, "n": 0 },
    "ane_residency_pct":  { "median": null, "n": 0 }
  },
  "model":   { "path": "...", "params": 9608704 },
  "workload":{ "mode": "single", "batch_size": 1, "prompt_tokens": 64,
                "gen_tokens": 100, "n_runs": 5, "warm_runs": 1 },
  "system":  { "hardware_model": "Mac17,8", "macos_build": "25F71",
                "physical_ram_gb": 48 },
  "runs": [ /* one entry per run with per-run breakdown */ ],
  "warnings": [ "n=5 is small; …", "git tree is dirty — …" ]
}

The full JSON has a runs array with one entry per run for per-run inspection (raw decode_ms, ITL median per run, etc.) — useful for detecting drift over the run series.

What this proves

What this does NOT prove

These are the next milestones. They’re scaffolded for; not in scope for this commit.

Caveats noted during the first run

Next steps (post-scaffold)

  1. Wire MLXLMEngine — subprocess mlx_lm.generate with per-token stdout timestamps; same protocol surface, no change to the Reporter.
  2. Wire LlamaCppEngine via llama-cli --simple-io.
  3. Add an --enable-energy run on a Mac with sudo configured and compare energy/token to the literature.
  4. Implement --mode sustained --duration 600 for the thermal-throttle regression.
  5. Add a downloader for ShareGPT-v3 prompts so we stop using the synthetic byte filler for paper-quality runs.