DEV Community

bigkijimon
bigkijimon

Posted on • Edited on

How many tok/s does a local LLM get on an M1 Max 64GB — I measured four models and the bigger one won

Originally published on Zenn (Japanese). Cross-posted here.

How many tokens per second does a local LLM actually get on a four-year-old M1 Max (64GB) today? When I went looking for benchmark sites, they were all full of M4/M5-generation numbers — the M1 Max row was simply missing. If it doesn't exist, you have to measure it yourself, so I benchmarked four models I had on hand under identical conditions. The table that came out had one counter-intuitive line in it: a model that is 3.6× larger on disk runs 1.5× faster than a smaller one. This article is that measurement, and why it happens.

Every number here is measured on my own machine (M1 Max 64GB / Ollama 0.30.8). Each figure is the median of three runs — no guesses, no borrowing numbers from other machines. Where I couldn't measure something, I say so.

The bottom line: measured tok/s for four models on hand

The measurement conditions are the same for every model. I sent the same Japanese prompt to /api/generate (num_predict=300, temperature=0.7, think=false) and computed decode (generation) tok/s from the response's eval_count ÷ eval_duration. I took the median of three runs each. Before measuring, I confirmed GPU utilization was 0% with no other generation jobs running.

Model Arch Total params active/token Size decode tok/s
qwen3.6 MoE 36.0B ~3B 23.9GB 60.4
qwen3.6-uncensored-cc MoE 34.7B ~3B 21.2GB 59.5
gemma4 dense 8.0B 8.0B 9.6GB 56.6
qwen3.5 dense 9.7B 9.7B 6.6GB 40.5

The three runs were, in order: qwen3.6 at 60.4/60.1/60.5, uncensored at 59.5/59.6/59.1, gemma4 at 56.6/56.5/56.6, qwen3.5 at 40.6/40.5/40.5. The spread is tiny in every case — 0.1 to 0.5 tok/s. These are medians, not single shots, so they're trustworthy. All are Q4_K_M quantization.

Reading the table top to bottom doesn't parse naively. The largest model on disk, qwen3.6 (23.9GB), is the fastest (60.4), and the smallest, qwen3.5 (6.6GB), is the slowest (40.5). On-disk size and speed run in reverse order. That's the whole point.

Why the bigger one is faster: decode is governed by active params

An LLM's decode speed (the rate at which it emits tokens one at a time) is roughly "memory bandwidth ÷ the bytes of parameters actually read per token." On the same machine — same memory bandwidth — the fewer parameters you touch per token, the faster you go.

This is where the MoE (Mixture of Experts) vs. dense distinction matters. ollama show makes the architectures clearly different.

  • qwen3.6: qwen35moe, with expert_count=256 and expert_used_count=8. It has 36B total parameters, but to produce one token it uses only 8 of the 256 experts. The parameters actually in motion are about 3B.
  • qwen3.5: qwen35 (dense, no expert keys). It uses all 9.7B every token.
  • gemma4: gemma4, dense 8.0B. Also uses 8B every token.

So the "real work per token" is about 3B for qwen3.6, 8B for gemma4, and 9.7B for qwen3.5. The order of least work is exactly the order of fastest speed — and the measurements lined up that way. qwen3.6 is faster than qwen3.5 while carrying 3.6× the disk size because those 24GB of weights sit in storage (or rather in unified memory), but only a fraction of them (~3B) is read out per token.

Horizontal bar chart comparing decode tok/s for four models. From top: qwen3.6 (60.4, MoE 24GB), qwen3.6-unc (59.5, MoE 21GB), gemma4 (56.6, dense 9.6GB), qwen3.5 (40.5, dense 6.6GB). Ordering by disk size does not match the speed ordering.
Even ordered by disk size (largest at top), the order does not match decode speed. What determines speed is not total size but active params per token. The MoE qwen3.6 is "big but fast."

Dense vs. dense follows size, as expected

Set MoE aside and the two dense models line up by the textbook. gemma4 (8.0B) does 56.6, qwen3.5 (9.7B) does 40.5. The smaller dense model is faster. 8.0B vs. 9.7B is about a 1.2× ratio, but the measurement opened up to 56.6 vs. 40.5 — about 1.4×. The gap being larger than the parameter ratio is because per-model differences (attention implementation, KV-cache handling, etc.) ride on top; here I'll only say "roughly ordered by size." I make no claim of strict proportionality.

Put another way: "I want a lighter model = pick the one with the smaller disk size" is only correct within dense models. The moment you put an MoE in the running, that intuition collapses. qwen3.6 eats 3.6× the disk of qwen3.5 yet beats it on speed.

Prefill (prompt reading) is a separate story

Everything above is decode (output) speed. Prefill (prompt eval), which reads the input, behaves differently, so I measured it separately.

Model prefill tok/s
gemma4 639
qwen3.6-uncensored 613
qwen3.6 543
qwen3.5 394

Prefill runs at roughly 10× decode speed (input tokens can be processed in parallel, batched). Here's one measurement pitfall, told honestly. When I first measured qwen3.6's prefill, I got an anomalous 4876 tok/s — about 8× the others. Obviously wrong. The cause: I had just run decode on the same model, so part of the prompt was still in cache and got treated as "already read." Re-measuring with a unique prompt settled it at 543 tok/s. Caching makes speed measurements lie, easily. So prefill should always be measured with fresh input (the table above is the re-measured value).

About the M1 Max "blank space"

The whole reason I started measuring was that searching benchmark sites turned up no M1 Max numbers. The tok/s tables for new models are usually filled with M4 Ultra / M5 Max / M4 Max, and the row for the 2021 M1 Max is blank, or at best a single value of dubious provenance. For someone weighing a used 64GB M1 Max, that's effectively nothing to go on.

These numbers are just a single point of measurement — one machine, M1 Max 64GB, Q4_K_M, Ollama 0.30.8 — and I'm not generalizing. Even so, the one point that "even on a four-year-old M1 Max, an active-3B MoE gets 60 tok/s, and 8B-class dense gets 40–57 tok/s" should be a far better clue than a blank. Subjectively, 60 tok/s streams output faster than you can read, and even at 40 tok/s there's almost no sense of waiting. It's practical for local use.

Reproducible takeaways

  1. Decode speed is governed by active params/token, not disk size. If you're picking a model and think "small = fast," first check whether it's MoE or dense. An MoE can be "big but fast."
  2. Watch for cache in speed measurements. Prefill in particular, if you fire the same prompt repeatedly, gives absurdly fast values from the previous run's cache. Measure with unique input every time.
  3. Use the median of three runs. A single shot can catch an outlier. Here the spread stayed within 0.1–0.5, so I trust these four values.

If your environment (model, Ollama/MLX version, quantization) differs, the numbers will change. If anyone is running other models on an M1 Max, I'd love to know your measured values — especially MoE models (beyond qwen3.6) and tok/s at different quantizations. There simply isn't enough M1-Max-generation data out there yet.

Top comments (0)