omeganya nya's lab notes

New tiny models on a CPU, part 1: a 2-bit file that isn't, a fast tiny MoE, and a thinker that wins

Preliminary. This is a smoke test: 22 easy questions per file, one pass each. It finds broken files and setup problems; it can't rank models. The real worker tests (pulling data out of text, tool calls, fixing their own mistakes, resisting planted instructions) come in part 2.

Three new families of tiny models arrived this week, so I ran them through my easy suite on the same small CPU-only server as before, next to the tiny models I had already tested:

LFM3 needed a newer llama.cpp than the one I had used so far, so this whole run is on build b11946. The three reference models gave exactly the same answers on the new build as on the old one, so the numbers below are comparable with my earlier posts.

The test

The 11 easy questions check the basics an agent needs: answer in a given format, follow a system prompt, stop after a list, echo accents and Japanese characters, use the conversation history, call a tool when asked, not call one for small talk, use a tool's result, and produce JSON. Each has a twin with different wording that I keep sealed: I only look at its total. Greedy decoding, thinking off (unless noted), 4 threads, one request at a time.

Model (files)Easy questions passed (of 11)Held-out twins (of 11)Decode tok/sPeak memory
Qwen2.5-0.5B (8 files)9–118–1022–461.0–1.7 GB
Qwen3-0.6B Q8_0108274.3 GB
SmallThinker-4B-A0.6B (10 files)8–117–815–273.0–5.5 GB
LFM3-1.7B, thinking off (7 files)9–107–86–171.7–3.8 GB
LFM3-1.7B, thinking on (7 files)1110–116–161.7–3.8 GB
Reference: Qwen3.5-0.8B Q8_01110211.4 GB
Reference: LFM2.5-350M Q5_K_M118570.7 GB

Memory is with a 32,768-token context; that matters a lot, as you'll see. All rows are in the CSV linked at the end.

Finding 1: the "2-bit" file that isn't

Last time, a 2-bit file (IBM's Granite 4.2 3B at Q2_K) couldn't stop talking: it had lost its end-of-turn token. So I expected Qwen2.5-0.5B's "q2_k" to break too. Instead it passed all 11 easy questions and 9 of the 11 sealed ones, as well as its own 8-bit file.

The file explains it: it contains no 2-bit tensors at all.

Qwen2.5-0.5B fileWhat its tensors actually are
"q2_k" (396 MB)IQ4_NL ×121, Q3_K ×24, Q5_0 ×24, Q8_0 ×1
"q3_k_m" (413 MB)IQ4_NL ×97, Q5_0 ×46, Q4_K ×23, Q5_1 ×2, Q5_K ×1
"q4_k_m" (469 MB)Q5_0 ×133, Q8_0 ×13, Q6_K ×12, Q4_K ×12
"q4_0" (409 MB)Q4_0 ×169, Q8_0 ×1

The K-quant types (Q2_K, Q3_K, Q4_K, …) work on blocks of 256 numbers. This model is 896 wide, which is 3.5 blocks, so for most tensors the quantizer quietly falls back to another type. The "q2_k" file ends up mostly 4-bit and is nearly the same size as "q4_0". The "q4_k_m" file is mostly Q5_0, a type that reads prompts slowly on this CPU, and it did: 112 tokens/s against 174 for "q4_0".

Lesson: for a small model, read the tensor types (any GGUF viewer shows them) before trusting the file name. This says nothing about real 2-bit weights. Granite's Q2_K showed what those can do.

Finding 2: the tiny MoE is fast, chatty with tools, and memory-hungry

SmallThinker's makers say that at Q4_0 it runs at "over 20 tokens/s on ordinary consumer CPUs". On my 4-vCPU cloud server, Q4_0 wrote 24.6 tokens/s. Claim confirmed, for short answers at least.

0.2 0.5 1 2 5 5 10 20 50 100 model file size (GB) decode, tokens/s 18 GB/s ÷ size LFM2.5-230M-Q6_K: 0.19 GB, 73.0 tok/s LFM2.5-350M-Q5_K_M: 0.26 GB, 56.5 tok/s Qwen3.5-0.8B-Q8_0: 0.83 GB, 21.2 tok/s qwen2.5-0.5b-instruct-q2_k: 0.42 GB, 41.2 tok/s qwen2.5-0.5b-instruct-q3_k_m: 0.43 GB, 39.8 tok/s qwen2.5-0.5b-instruct-q4_0: 0.43 GB, 45.8 tok/s qwen2.5-0.5b-instruct-q4_k_m: 0.49 GB, 36.2 tok/s qwen2.5-0.5b-instruct-q5_k_m: 0.52 GB, 33.8 tok/s qwen2.5-0.5b-instruct-q6_k: 0.65 GB, 37.4 tok/s qwen2.5-0.5b-instruct-q8_0: 0.68 GB, 35.6 tok/s qwen2.5-0.5b-instruct-fp16: 1.27 GB, 22.4 tok/s Qwen3-0.6B-Q8_0: 0.64 GB, 27.1 tok/s SmallThinker-4B-A0.6B-Instruct.Q3_K_S: 1.94 GB, 20.4 tok/s SmallThinker-4B-A0.6B-Instruct.Q3_K: 2.12 GB, 24.3 tok/s SmallThinker-4B-A0.6B-Instruct.IQ4_XS: 2.37 GB, 19.8 tok/s SmallThinker-4B-A0.6B-Instruct.Q4_0: 2.47 GB, 24.6 tok/s SmallThinker-4B-A0.6B-Instruct.Q4_K_S: 2.49 GB, 26.7 tok/s SmallThinker-4B-A0.6B-Instruct.IQ4_NL: 2.49 GB, 26.0 tok/s SmallThinker-4B-A0.6B-Instruct.Q4_K: 2.63 GB, 14.9 tok/s SmallThinker-4B-A0.6B-Instruct.Q5_K: 3.06 GB, 22.2 tok/s SmallThinker-4B-A0.6B-Instruct.Q6_K: 3.51 GB, 24.0 tok/s SmallThinker-4B-A0.6B-Instruct.Q8_0: 4.55 GB, 19.6 tok/s LFM3-1.7B-QAD-Q4_0: 1.05 GB, 17.4 tok/s LFM3-1.7B-Q4_K_M: 1.10 GB, 17.2 tok/s LFM3-1.7B-Q5_K_M: 1.26 GB, 9.7 tok/s LFM3-1.7B-Q6_K: 1.43 GB, 13.3 tok/s LFM3-1.7B-Q8_0: 1.85 GB, 11.0 tok/s LFM3-1.7B-BF16: 3.47 GB, 6.2 tok/s LFM3-1.7B-F16: 3.47 GB, 7.0 tok/s SmallThinker 4B-A0.6B LFM3-1.7B Qwen2.5-0.5B
Decode speed against file size, thinking off (log scales). dense models, SmallThinker (MoE), LFM3-1.7B. The dashed line is the 18 GB/s ÷ size rule that held for dense models in my speed post. Hover a dot for the file.

The chart shows why. For a dense model, decode speed is memory bandwidth divided by file size. SmallThinker's files range from 1.9 to 4.6 GB, yet nine of the ten decode at 20–27 tokens/s (the tenth, Q4_K, at 15). Only the 0.6B active parameters are read per token, so the file size hardly matters. Its Q8_0 file writes faster than the dense rule predicts for a file a quarter of its size.

Two things to watch for part 2:

Its scores were middling, 8–11 of 11. Part 2 will show whether the speed comes with usable answers on real tasks.

Finding 3: LFM3 is the final boss, but only when it may think

With thinking off, LFM3 scored 9–10 of 11, no better than the tiny models. Its mistakes all looked like this:

I first suspected my setup, so I looked at the prompt the server builds. It was correct: with thinking off, the model's turn starts with an empty <think></think>. LFM3 does its working in the visible answer anyway. It is built to think.

With thinking on, nothing else changed, **all 7 files answered all 11 easy questions, and 6 of 7 also got all 11 sealed twins.** No other model came close. The price: about 120 tokens of thinking per answer, so the Q4_K_M file took 3.6 minutes for the 22 questions instead of 0.9. In part 2 we'll see whether that ratio holds on harder tasks, where thinking might pay off more.

Finding 4: memory you don't see in the file size

Two effects made some models need several times their file size:

Coming in part 2

The easy suite only finds broken files. Part 2 runs the tests that decide what a tiny model can be used for:

I'll run them on the best file of each new model, with LFM3 thinking, against the same tasks my earlier tiny models did. I'll also check whether SmallThinker's speed holds once prompts are a few hundred tokens long. My guess, to be checked: LFM3 wins on quality but costs several times the time, and SmallThinker is the most interesting for speed.

The data

Every file's row (size, passes, prefill and decode speed, peak memory, time, tokens generated, thinking tokens, CPU steal, llama.cpp build) is in 2026-10-28-tiny-models-part1.csv. Speeds come from 22 short tasks with answers of about 10 tokens, so treat them as ±15%.