LFM2 vs Qwen3.5 on an Apple M4: Every Small Model, Both Backends
Liquid AI's LFM2 model card claims 2x faster decode and prefill on CPU than Qwen3. I wanted to check that on my own hardware before repeating it. So: every LFM2 checkpoint under 2B, the two smallest Qwen3.5 models, llama-bench on my M4 MacBook (24GB), once on the Metal GPU and once CPU-only.
Raw JSONL from every run is in the post's asset folder: llama-bench-results.jsonl.
TL;DR
- At equal parameter count (LFM2-700M vs Qwen3.5-0.8B, both around 745M params), LFM2 decodes 1.2x faster on CPU and 1.6x faster on Metal. Not the 2x on the model card, but the gap is real and it showed up in every run.
- On CPU, Q8_0 prefill is about 2.5x faster than Q4_K_M for every model here. LFM2-1.2B does 175 tok/s prefill at Q8_0 and 68 tok/s at Q4_K_M. I did not expect this. If you run on CPU and care about time to first token, the smaller file is the slower file.
- LFM2-350M at Q8_0 is the only configuration that reads a 512-token prompt in under one second on CPU. Everything else takes 2 to 8 seconds before the first token appears.
- Quality: Q4_K_M costs 2.4% to 4.5% perplexity over Q8_0, so on Apple Silicon CPU Q4 is slower and worse. At matched size Qwen3.5-0.8B and LFM2-700M are within error on wikitext-2; LFM2 wins the vendors' IFEval, Qwen3.5-2B wins knowledge.
- Metal decode tops out at 146 tok/s (LFM2-350M Q4_K_M) and bottoms out at 31 tok/s (Qwen3.5-2B Q8_0). All of these are usable for chat. The interesting numbers are the CPU ones.
Setup
| Machine | MacBook, Apple M4 (4 performance + 6 efficiency cores), 24GB unified memory |
| Runtime | llama.cpp build 10621 (c1d0e7a), Homebrew, Metal + BLAS |
| Tool | llama-bench -p 512 -n 128 -r 5 -o jsonl |
| Metal runs | -ngl 99, all layers on GPU |
| CPU runs | -ngl 0 -t 4, one thread per performance core |
| LFM2 GGUFs | LiquidAI's official LFM2-*-GGUF repos |
| Qwen3.5 GGUFs | unsloth's Qwen3.5-*-GGUF repos |
Two workloads per model: pp512 (read a 512-token prompt, the time-to-first-token number) and tg128 (generate 128 tokens, the chat speed number). Five repetitions each.
The lineup
Both families are hybrids that swap most attention layers for something cheaper, so this is a fair fight.
| Model | Params | Layers | Layer mix | Vocab | GGUF Q4_K_M | GGUF Q8_0 |
|---|---|---|---|---|---|---|
| LFM2-350M | 354M | 16 | 10 gated short-conv + 6 GQA attention | 65,536 | 216 MiB | 359 MiB |
| LFM2-700M | 742M | 16 | 10 gated short-conv + 6 GQA attention | 65,536 | 445 MiB | 753 MiB |
| LFM2-1.2B | 1,170M | 16 | 10 gated short-conv + 6 GQA attention | 65,536 | 695 MiB | 1,186 MiB |
| Qwen3.5-0.8B | 752M | 24 | 18 Gated DeltaNet + 6 gated attention | 248,320 | 497 MiB | 764 MiB |
| Qwen3.5-2B | 1,882M | 24 | 18 Gated DeltaNet + 6 gated attention | 248,320 | 1,211 MiB | 1,908 MiB |
Two numbers in that table predict the speed results before you run anything.
The vocabulary: Qwen3.5's 248k-token embedding is 3.8x larger than LFM2's 65k. On Qwen3.5-0.8B that is 254M parameters, a third of the model, sitting in the embedding and output projection, and the output projection is a full matmul on every decoded token. LFM2-700M spends 100M there.
The depth: LFM2 runs 16 layers to Qwen3.5's 24 at the same parameter count. Fewer, wider layers means fewer kernel launches per token, which matters most on Metal.
Decode speed (tg128)


The head-to-head at matched size:
| LFM2-700M | Qwen3.5-0.8B | LFM2 advantage | |
|---|---|---|---|
| Metal tg128, Q4_K_M | 102.2 tok/s | 63.6 tok/s | 1.61x |
| Metal tg128, Q8_0 | 73.6 tok/s | 52.1 tok/s | 1.41x |
| CPU tg128, Q4_K_M | 53.1 tok/s | 43.1 tok/s | 1.23x |
| CPU tg128, Q8_0 | 59.0 tok/s | 44.8 tok/s | 1.32x |
The model card's 2x does not reproduce here. The card compared against Qwen3, whose small models were plain transformers; Qwen3.5 moved to a Gated DeltaNet hybrid and closed much of the gap. What holds up: LFM2 is the faster architecture at equal parameter count, on both backends, at both quant levels.
LFM2-1.2B vs Qwen3.5-2B, the biggest model in each family: 35 vs 23 tok/s on CPU, 70 vs 37 on Metal. Qwen3.5-2B has 1.6x the parameters though, so read that as a per-watt tradeoff, not an architecture verdict.
Prefill speed (pp512) and time to first token
This is where the quantization result from the TL;DR comes from.

| Model | CPU pp512, Q4_K_M | CPU pp512, Q8_0 | Q8_0 speedup |
|---|---|---|---|
| LFM2-350M | 222 tok/s | 557 tok/s | 2.50x |
| LFM2-700M | 107 tok/s | 274 tok/s | 2.55x |
| Qwen3.5-0.8B | 129 tok/s | 224 tok/s | 1.74x |
| LFM2-1.2B | 68 tok/s | 175 tok/s | 2.59x |
| Qwen3.5-2B | 61 tok/s | 141 tok/s | 2.30x |
Prefill is compute-bound: 512 tokens go through every weight matrix in one batched matmul, so bytes-per-weight barely matters and unpacking 4-bit nibbles dominates. Q8_0 skips most of that. A 4-bit model on CPU pays a 2.5x tax on every prompt it reads.
As time to first token on a 512-token prompt (a short system prompt plus a paragraph of input):

| Model | Q4_K_M TTFT | Q8_0 TTFT |
|---|---|---|
| LFM2-350M | 2.3 s | 0.92 s |
| LFM2-700M | 4.8 s | 1.9 s |
| Qwen3.5-0.8B | 4.0 s | 2.3 s |
| LFM2-1.2B | 7.6 s | 2.9 s |
| Qwen3.5-2B | 8.4 s | 3.6 s |
Nothing here hits 100 ms on CPU with a 512-token prompt; that is a short-prompt or GPU/NPU story. But LFM2-1.2B at Q8_0 reads a prompt faster than Qwen3.5-0.8B at Q4_K_M, despite 1.5x the parameters.
One oddity: Qwen3.5-0.8B at Q4_K_M is the only place Qwen beats LFM2 at matched size, 129 vs 107 tok/s prefill. At Q8_0 the order flips back (274 vs 224). My guess is that Gated DeltaNet's chunked prefill kernel is well-tuned and LFM2's short-conv path does more dequant work at Q4.
On Metal, prefill is a different world:

Every model reads a 512-token prompt in under one second on Metal; LFM2-350M does it in 160 ms. The Q4 vs Q8 gap mostly vanishes on GPU, which has compute to burn.
Footprint vs speed

For a CPU-only deployment with a memory budget, this is the chart:
- Under 500 MiB: LFM2-350M Q8_0 (359 MiB, 119 tok/s) is the pick. LFM2-700M Q4_K_M (445 MiB, 53 tok/s) fits too but you pay the 2.5x prefill tax.
- Under 800 MiB: LFM2-700M Q8_0 (753 MiB, 59 tok/s, 1.9 s TTFT) beats Qwen3.5-0.8B Q8_0 (764 MiB, 45 tok/s, 2.3 s TTFT) on every axis at almost identical size.
- Under 1.2 GiB: LFM2-1.2B Q8_0 (1,186 MiB) at 37 tok/s and 2.9 s TTFT, versus Qwen3.5-2B Q4_K_M (1,211 MiB) at 23 tok/s and 8.4 s TTFT. Qwen3.5-2B is the more capable model on paper (1.9B params, 262k context, vision), so this is the one tradeoff where "slower but smarter" is a real option.
Does speed cost quality?
Fast is only useful if the model is still worth running. Three checks, cheapest first.
Vendor benchmarks, where they overlap
Both model cards report IFEval and MMMLU in non-thinking mode. Nothing else lines up (Liquid reports MMLU, Qwen reports MMLU-Pro and MMLU-Redux). Different labs, different harnesses, so treat the gap as a hint, not a measurement: both cards score Qwen3-1.7B on IFEval, and Liquid gets 73.98 where Qwen gets 68.2 for the same model.
| Model | IFEval | MMMLU | GPQA |
|---|---|---|---|
| LFM2-350M | 65.1 | 38.0 | 27.5 |
| LFM2-700M | 72.2 | 43.3 | 28.5 |
| LFM2-1.2B | 74.9 | 46.7 | 31.5 |
| Qwen3.5-0.8B | 52.1 | 34.1 | 11.9 (thinking) |
| Qwen3.5-2B | 61.2 | 56.9 | 51.6 (thinking) |
Source: each model's Hugging Face card. Qwen reports GPQA only in thinking mode, so that column is not apples to apples. The picture: LFM2 wins instruction following at every size, Qwen3.5-2B wins knowledge, and the Qwen numbers come from a model that also does vision and 201 languages with the same weights.
Perplexity: what quantization actually costs
This one I can measure myself. llama-perplexity on wikitext-2, context 512, 120 chunks, all 10 GGUFs.

| Model | Q4_K_M | Q8_0 | Q4 penalty |
|---|---|---|---|
| LFM2-350M | 25.24 | 24.45 | +3.2% |
| LFM2-700M | 20.09 | 19.23 | +4.5% |
| Qwen3.5-0.8B | 19.68 | 18.99 | +3.7% |
| LFM2-1.2B | 15.97 | 15.53 | +2.8% |
| Qwen3.5-2B | 13.10 | 12.79 | +2.4% |
At matched size, Qwen3.5-0.8B (18.99) edges LFM2-700M (19.23), inside the error bars. Wikitext is English Wikipedia and Qwen has the bigger vocabulary, so I expected a wider gap. The useful result is the Q4 column: on this hardware Q4_K_M is a strictly worse point, slower on CPU and a few percent worse on perplexity, unless the file size itself is the constraint.
Six-prompt smell test
Three writing prompts, three coding prompts, every model at Q8_0, temperature 0, one shot. Coding answers were run against asserts. Writing answers were checked for the stated constraint (no "hydration", exactly three lines) and read by me. Raw outputs are in smell_results.json.
| Prompt | LFM2-350M | LFM2-700M | Qwen3.5-0.8B | LFM2-1.2B | Qwen3.5-2B |
|---|---|---|---|---|---|
| Product blurb, banned word | fail | pass | fail | pass | pass |
| Explain mitochondria to a 10-year-old | pass | pass | pass | pass | pass |
| Haiku, exactly 3 lines | pass | pass | pass | pass | pass |
is_palindrome | pass | pass | pass | pass | pass |
fizzbuzz | pass | fail | pass | pass | pass |
oldest(people) | pass | pass | fail | pass | pass |
| Total | 5/6 | 5/6 | 4/6 | 6/6 | 6/6 |
Six prompts is not a benchmark, but the failures were telling. The two sub-500M models both wrote "hydrated" or "hydration" after being told not to. LFM2-700M's fizzbuzz never emitted "FizzBuzz" and returned ints instead of strings. Qwen3.5-0.8B's oldest returned the whole dict instead of the name. LFM2-700M also closed its mitochondria answer with "Let me know if you'd like another simpler way to explain it!", which is a chatbot habit leaking into a one-shot task. Both 1B-class models went clean.
Full results
| Model | Quant | Size | Params | Metal pp512 | Metal tg128 | CPU pp512 | CPU tg128 | CPU TTFT @ 512 |
|---|---|---|---|---|---|---|---|---|
| LFM2-350M | Q4_K_M | 216 MiB | 354M | 3163 | 146.2 | 222 | 103.1 | 2303 ms |
| LFM2-350M | Q8_0 | 359 MiB | 354M | 3512 | 126.7 | 557 | 119.1 | 920 ms |
| LFM2-700M | Q4_K_M | 445 MiB | 742M | 1017 | 102.2 | 107 | 53.1 | 4769 ms |
| LFM2-700M | Q8_0 | 753 MiB | 742M | 1140 | 73.6 | 274 | 59.0 | 1869 ms |
| Qwen3.5-0.8B | Q4_K_M | 497 MiB | 752M | 925 | 63.6 | 129 | 43.1 | 3983 ms |
| Qwen3.5-0.8B | Q8_0 | 764 MiB | 752M | 1238 | 52.1 | 224 | 44.8 | 2284 ms |
| LFM2-1.2B | Q4_K_M | 695 MiB | 1170M | 629 | 70.2 | 68 | 34.6 | 7563 ms |
| LFM2-1.2B | Q8_0 | 1186 MiB | 1170M | 658 | 47.8 | 175 | 37.3 | 2924 ms |
| Qwen3.5-2B | Q4_K_M | 1211 MiB | 1882M | 510 | 37.4 | 61 | 22.9 | 8373 ms |
| Qwen3.5-2B | Q8_0 | 1908 MiB | 1882M | 585 | 30.7 | 141 | 23.2 | 3636 ms |
Tokens/sec, mean of 5 runs. CPU stddev under 5% on every row. Metal pp512 stddev 10 to 25% on the two smallest models (runs so short that scheduling noise shows), under 15% elsewhere.
What I would actually deploy
On Apple Silicon CPU, default to Q8_0, not Q4_K_M. Decode is the same speed or faster, prefill is 2.5x faster, and you skip the Q4 quality loss. The only reason left to pick Q4 is a hard memory ceiling.
LFM2 is the faster architecture per parameter, by 1.2x to 1.6x. The 2x on the model card was against Qwen3's plain transformers; against Qwen3.5's hybrid the gap narrowed but did not close. I suspect the smaller vocabulary does a good chunk of the work.
If you need TTFT under a second on CPU, you need a sub-500M model. LFM2-350M at Q8_0 is the only one here that does it, at 0.9 s for 512 tokens. From there the levers are shorter prompts, prompt caching, or moving prefill to the GPU or NPU.
Reproduce it
brew install llama.cpp # need a build new enough for the qwen35 arch
pip install -U huggingface_hub
for m in 350M 700M 1.2B; do
for q in Q4_K_M Q8_0; do
hf download LiquidAI/LFM2-$m-GGUF LFM2-$m-$q.gguf --local-dir models
done
done
for m in 0.8B 2B; do
for q in Q4_K_M Q8_0; do
hf download unsloth/Qwen3.5-$m-GGUF Qwen3.5-$m-$q.gguf --local-dir models
done
done
cd models
for f in *.gguf; do
llama-bench -m $f -ngl 99 -t 4 -p 512 -n 128 -r 5 -o jsonl # Metal
llama-bench -m $f -ngl 0 -t 4 -p 512 -n 128 -r 5 -o jsonl # CPU only
doneSet -t to your performance core count (sysctl hw.perflevel0.physicalcpu). Run it on an idle machine or the CPU numbers will lie to you.
Related posts
TurboQuant+ Meets Gemma on a Modal L40S
Second pass on TurboQuant+ KV cache compression, this time on a rented L40S across Gemma 3 12B, Gemma 4 E4B, and Gemma 4 26B MoE. One works beautifully, two break in interesting ways.
Benchmarking TurboQuant+ KV Cache Compression on Apple Silicon
I tested TurboQuant+ KV cache compression across 1.5B, 7B, and 14B models on an M4 MacBook Air. The speed gains are real, but there are sharp cliffs you need to know about.
Multimodal Models Are the New Default: GPT-4V, Gemini, and Beyond
In 2024, the best AI models understand text, images, audio, and video natively. As someone with a CV background, this convergence feels like a turning point.