File size: 24,268 Bytes
56b1d37
 
 
 
 
 
 
 
 
ba53d62
56b1d37
 
 
 
 
ba53d62
56b1d37
ba53d62
56b1d37
 
 
 
ba53d62
56b1d37
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
61e603e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca362dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
ba53d62
 
 
 
 
56b1d37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba53d62
 
 
 
56b1d37
 
 
 
 
 
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
 
 
ba53d62
 
 
 
56b1d37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca362dd
 
 
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
ca362dd
 
 
 
56b1d37
ca362dd
 
 
 
56b1d37
ca362dd
 
 
 
 
 
 
56b1d37
 
 
ca362dd
 
 
 
 
 
 
 
 
 
56b1d37
ca362dd
 
 
56b1d37
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
 
 
 
 
ca362dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
 
 
 
 
 
 
 
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
 
 
 
 
 
ba53d62
 
 
56b1d37
 
 
 
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
 
 
 
 
61e603e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4077b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
ba53d62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56b1d37
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
---
base_model: XiaomiMiMo/MiMo-V2.5
base_model_relation: quantized
pipeline_tag: text-generation
library_name: gguf
tags:
  - gguf
  - rocm
  - rocmfp4
  - rocmfpx
  - strix-halo
  - gfx1151
  - moe
---

# MiMo-V2.5 โ€” ROCmFP4 / ROCmFPX GGUF (Strix Halo / gfx1151)

ROCmFP4 and ROCmFPX quantizations of [MiMo-V2.5](https://huggingface.co/XiaomiMiMo/MiMo-V2.5)
(310B total / 15B active MoE), built for **AMD Strix Halo (Ryzen AI Max+ 395,
gfx1151)** with the [ROCmFPX](https://github.com/charlie12345/ROCmFPX) fork of
llama.cpp.

There are two ways to run this model on Strix Halo, and they want different files:

| you have | use | why |
| --- | --- | --- |
| **two 128 GB boxes** + USB4/Thunderbolt | `ROCmFP4-FAST/` | best quality; 153 GiB does not fit one node |
| **one 128 GB box** | `Q2_0_ROCMFPX-COHERENT/` | 90.9 GiB fits a single node โ€” and is **+37% faster at decode** than the two-node setup |

The single-node option being *faster* is not a mistake. llama.cpp's RPC layer split
runs the nodes **sequentially**, so a second box buys **capacity, not speed**. If the
model fits on one node, one node wins.

> **Note on quality.** The `ROCmFP4` and `ROCmFP4_FAST` variants are *not*
> imatrix-calibrated; Unsloth's `UD-Q4_K_XL` is, and is tensor-aware on top of that.
> They win on **speed and size** โ€” benchmark both if marginal output quality matters
> more to you than tok/s.
>
> `Q2_0_ROCMFPX-COHERENT` **is** imatrix-calibrated (it has to be โ€” see below). But
> it is 2.5 bpw against FAST's 4.26, and that reduction is real. It passes coherence
> checks on code, factual recall, arithmetic and JSON, but **no pass@1 evaluation has
> been run**, so treat the quality gap as *unquantified rather than absent*.

## โš ๏ธ Requirements โ€” stock llama.cpp will not load these

`Q4_0_ROCMFP4` and `Q4_0_ROCMFP4_FAST` are quantization types defined by the
**[ROCmFPX](https://github.com/charlie12345/ROCmFPX)** fork. Upstream llama.cpp,
Ollama, LM Studio and every downstream that vendors mainline ggml **cannot read
these files** โ€” you will get an unknown-ggml-type error, not a slow model. You
must build the fork.

They also target **gfx1151** specifically (Ryzen AI Max+ 395 / Strix Halo). The
formats are built around that hardware's dequant path; on other GPUs, expect
either a build failure or no benefit.

### Build

```bash
git clone https://github.com/charlie12345/ROCmFPX.git
cd ROCmFPX
git checkout 3edc3d31ee5ebcea47fd7e0f42c89767bb4245db   # the commit these were built and tested with

cmake -S . -B build \
    -DCMAKE_BUILD_TYPE=Release \
    -DGGML_HIP=ON \
    -DGGML_RPC=ON \
    -DGGML_HIP_FORCE_MMQ=ON \
    -DGGML_HIP_ROCWMMA_FATTN=OFF \
    -DGGML_VULKAN=OFF -DGGML_CUDA=OFF \
    -DCMAKE_HIP_ARCHITECTURES=gfx1151 \
    -DGPU_TARGETS=gfx1151 \
    -DLLAMA_BUILD_SERVER=ON \
    -DLLAMA_BUILD_WEBUI=OFF -DLLAMA_USE_PREBUILT_WEBUI=OFF \
    -DLLAMA_BUILD_TESTS=OFF -DGGML_BUILD_TESTS=OFF

cmake --build build -j "$(nproc)" --target \
    llama-cli llama-server llama-bench llama-quantize rpc-server
```

`-DGGML_RPC=ON` is what gives you `rpc-server` and the `RPC0` device โ€” required
for the two-node split. Built against **ROCm 6.4**.

**Use the ROCm backend, not Vulkan.** RADV imposes a per-buffer allocation ceiling
that a model this size runs straight into; `-dev Vulkan0` will OOM where `ROCm0`
works fine.

### Making the memory available

A single Strix Halo box defaults to a GPU carve-out far below what these need.
Either set `GGML_CUDA_ENABLE_UNIFIED_MEMORY=1` in the environment, or raise the
GTT limit on the kernel command line (what we run):

```
amdgpu.gttsize=126976 ttm.pages_limit=32505856 ttm.page_pool_size=32505856
```

With the cmdline set, `UNIFIED_MEMORY` is no longer needed. We measured no
throughput difference between the two approaches โ€” the cmdline is just less
fragile.

### โšก Force the GPU power level โ€” worth 14% decode

**This is the single highest-value host setting, and it is easy to miss.**

```bash
echo high | sudo tee /sys/class/drm/card*/device/power_dpm_force_performance_level
```

| `power_dpm_force_performance_level` | pp512 | tg128 |
| --- | ---: | ---: |
| `auto` (default) | 322.87 | 20.94 |
| **`high`** | **329.43** | **23.87** |

**+14% decode, +2% prefill, from one sysfs write.** The DPM governor never ramps
to peak on MoE decode โ€” the workload is bursty and low-occupancy, so `auto` reads
it as near-idle and leaves the GPU and fabric below spec. Effective memory
bandwidth goes from 167 to 190.7 GB/s (65% -> 75% of the 256 GB/s theoretical).
DRAM is at its rated 8000 MT/s either way; this is purely a clock-governor effect.

The gain is specific to **single-stream** decode. Under concurrency the GPU is
already loaded enough that `auto` ramps by itself โ€” aggregate throughput at C8 is
unchanged.

Transparent hugepages were tested alongside this and make **no difference**
(21.24 with THP alone vs 20.94 baseline), despite the 153 GiB working set. Not
worth the system-wide side effects.

It resets on reboot. To persist:

```ini
# /etc/systemd/system/amdgpu-perf-high.service
[Unit]
Description=Force amdgpu DPM to high
After=multi-user.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'for c in /sys/class/drm/card*/device/power_dpm_force_performance_level; do echo high > "$c"; done'
ExecStop=/bin/sh -c 'for c in /sys/class/drm/card*/device/power_dpm_force_performance_level; do echo auto > "$c"; done'

[Install]
WantedBy=multi-user.target
```

Apply it on **every** node โ€” the RPC peer's clocks matter just as much as the
head node's.

## Variants

| variant | effective bpw | size | shards | fits 1 node? | folder |
| --- | ---: | ---: | ---: | :---: | --- |
| **`Q4_0_ROCMFP4_FAST`** โ† best quality | 4.26 | **153.47 GiB** (164,795,562,720 B) | 4 | no | `ROCmFP4-FAST/` |
| **`Q2_0_ROCMFPX`** โ† fastest, single-node | 2.52 | **90.92 GiB** (97,626,242,048 B) | 3 | **yes** | `Q2_0_ROCMFPX-COHERENT/` |
| `Q4_0_ROCMFP4` | 5.24 | 189.02 GiB (202,969,561,824 B) | 5 | no | `ROCmFP4/` |

For reference, `unsloth/MiMo-V2.5-GGUF UD-Q4_K_XL` is **178.44 GiB** at 4.95 bpw โ€”
`ROCmFP4_FAST` is **14% smaller**.

> **`ROCmFP4_FAST` dominates `ROCmFP4` on every axis we measured** โ€” smaller,
> faster prefill, faster decode. Unless you specifically want the higher-precision
> tensors, take FAST. `ROCmFP4` is published for completeness.

**A note on the bpw labels.** `llama-quantize` advertises `Q4_0_ROCMFP4` as
"4.50 bpw", but measured against the 309.77 B parameter count the real figure is
**5.24 bpw** โ€” the recipe promotes several tensor classes (e.g. `ffn_gate` โ†’ `q5_K`)
rather than quantizing everything to ROCmFP4. `Q4_0_ROCMFP4_FAST` measures 4.26 bpw
against its 4.25 label, so that one is honest. This is why the "non-fast" build
ends up *larger* than `UD-Q4_K_XL` rather than smaller.

All were quantized from the BF16 GGUF (`unsloth/MiMo-V2.5-GGUF`, 14 shards,
619,638,702,336 bytes, verified byte-exact).

The 4-bit variants need no special handling:

```bash
llama-quantize MiMo-V2.5-BF16-00001-of-00014.gguf \
    MiMo-V2.5-ROCmFP4-FAST.gguf Q4_0_ROCMFP4_FAST 16
```

### `Q2_0_ROCMFPX` needs two extra flags or it produces garbage

This is the important part of this section. A plain `Q2_0_ROCMFPX` run โ€” no imatrix,
default tensor types โ€” yields a model that loads, benchmarks at a healthy 34.97 t/s,
and emits **this**:

```
๏ผŒ้—ฎ้ข˜๏ผŒ1.้—ฎ้ข˜๏ผŒ1.้—ฎ้ข˜๏ผŒ10000000000000000000000000000000000000...
```

Fluent-looking throughput, zero usable output. Both of the following are required:

```bash
llama-quantize \
    --imatrix MiMo-V2.5-imatrix.gguf \
    --token-embedding-type q6_k \
    --output-tensor-type q6_k \
    MiMo-V2.5-BF16-00001-of-00014.gguf \
    MiMo-V2.5-Q2_0_ROCMFPX-COHERENT.gguf Q2_0_ROCMFPX 16
```

- **`--imatrix`** โ€” sub-3-bit quantization needs importance-weighted rounding. The
  proof is that mainline `IQ2_XXS` stays coherent at **2.06 bpw**, *lower* than our
  2.5, because it is imatrix-aware. Uniform rounding survives 4 bpw; it does not
  survive 2.5. The imatrix used here is
  [bartowski's](https://huggingface.co/bartowski/MiMo-V2.5-GGUF) (`MiMo-V2.5-imatrix.gguf`,
  287 entries over 818 chunks).
- **`--token-embedding-type q6_k --output-tensor-type q6_k`** โ€” the embedding table
  (152,576 tokens) and LM head are the most precision-sensitive tensors in the model,
  and the imatrix does not cover them (`did not find weights for token_embd.weight`).
  Crush them to 2.5 bpw and nearby token vectors collapse into one another, which is
  exactly the wrong-language-token + repetition signature above. Cost: **+0.3 GB on a
  97 GB file.**

ROCmFPX's own naming corroborates this: `Q4_0_ROCMFP4_COHERENT` is defined as
"ROCmFP4 **+ Q6_K token embeddings**". Nobody names a variant *coherent* unless the
alternative was incoherent. `Q2_0_ROCMFPX` ships with no such provision, hence the
`-COHERENT` suffix on this folder.

## Measured performance

### Test setup

Two Beelink GTR 9 Pro (Ryzen AI Max+ 395, gfx1151, 128 GB unified) linked by a
single USB4 cable. The 4-bit variants are layer-split across both with llama.cpp RPC;
**`Q2_0_ROCMFPX` runs on one box with no RPC at all** (`-ngl 999`, no `-dev`), which is
the whole reason it decodes faster. **All numbers are single-stream** unless the
concurrency section says otherwise.

| | |
| --- | --- |
| engine | ROCmFPX (llama.cpp fork), ROCm 6.4, HIP backend |
| OS / kernel | Ubuntu 24.04, mainline 6.18.6 |
| topology | node2 = head, node1 = `rpc-server` over Thunderbolt (`192.168.2.1:50052`) |
| GPU carve-out | 126976 MiB via `amdgpu.gttsize` / `ttm.pages_limit` kernel cmdline |
| transport | TCP over `thunderbolt0` (RDMA measured, no difference) |

Benchmark command:

```bash
llama-bench -m <model.gguf> \
    -rpc 192.168.2.1:50052 -dev ROCm0/RPC0 \
    -ngl 999 -fa 1 -mmp 0 -r 1 -p 512 -n 128
```

`-mmp 0` (no mmap) is **required** โ€” with mmap the working set thrashes against
the 128 GB of RAM and never converges. Note `llama-bench` wants `-dev` entries
separated by `/`, while `llama-cli` wants `,`.

### Single-stream results

All rows below are measured with `power_dpm_force_performance_level=high` on both
nodes (see Requirements) โ€” without it every number drops 9โ€“14%.

| variant | nodes | size | prefill (pp512) | decode (tg128) |
| --- | :---: | ---: | ---: | ---: |
| **`Q2_0_ROCMFPX`** | **1** | 90.92 GiB | 241.05 t/s | **32.53 t/s** |
| **`Q4_0_ROCMFP4_FAST`** | 2 | 153.47 GiB | **328.73 t/s** | 23.80 t/s |
| _(reference)_ `UD-Q4_K_XL` | 2 | 178.44 GiB | 339.31 t/s | 17.16 t/s |
| `Q4_0_ROCMFP4` _(at dpm=auto)_ | 2 | 189.02 GiB | 248.10 t/s | 16.80 t/s |

**`Q2_0_ROCMFPX` on one node decodes 36.7% faster than `ROCmFP4_FAST` on two, for
26.7% less prefill.** Two effects compound: fewer bytes to stream per token (2.52 vs
4.26 bpw), and no RPC layer split โ€” which runs the nodes *sequentially*, so it costs
decode latency rather than saving it. Prefill is the opposite case: it is
compute-bound and genuinely benefits from a second GPU, which is why the two-node
rows win there.

Q2 error bars over `-r 2`: pp512 ยฑ0.75, tg128 ยฑ0.08.

**`ROCmFP4_FAST` is +38.7% decode over `UD-Q4_K_XL` for โˆ’3.1% prefill, while being
14% smaller.** The decode gain far exceeds what the size reduction alone predicts
(4.26 vs 4.95 bpw) โ€” the single-scale layout also dequantizes more cheaply on
gfx1151.

Both were re-measured at `dpm=high` so the comparison is like-for-like. Worth
noting `UD-Q4_K_XL` gains only ~9% from that tuning where `ROCmFP4_FAST` gains 14%,
consistent with the FAST dequant path being more clock-sensitive. At the old
`dpm=auto` default the gap read as +33%.

`ROCmFP4` (non-fast) has not been re-measured at `dpm=high`; its row is from
`dpm=auto` and is not comparable to the two above. It was the weakest of the three
on throughput at equal settings and is published only for completeness.

Reproducibility: `ROCmFP4_FAST` at `dpm=high` was measured three times โ€”
329.43/23.87, 329.14/23.89, 328.73/23.80 โ€” a 0.2% spread on pp512 and 0.4% on
tg128.

### Decode vs. context depth

`Q4_0_ROCMFP4_FAST`, with `power_dpm_force_performance_level=high`:

| depth | pp512 | tg128 |
| ---: | ---: | ---: |
| 0 | 328.73 | **23.80** |
| 8192 | 286.70 | **23.00** |
| 32768 | 214.53 | **21.72** |

Decode is remarkably flat with context โ€” a consequence of the 9-full/39-SWA
attention split, where only 9 layers grow with depth. Prefill decays normally.

For reference, the same curve at the default `dpm=auto` was 315.5/21.07,
267.1/20.68 and 197.7/19.53 โ€” the tuning is worth ~11โ€“13% of decode at every
depth.

**`Q2_0_ROCMFPX` on a single node**, same benchmark, `dpm=high`, `-r 2`:

| depth | pp512 | tg128 | vs FAST 2-node decode |
| ---: | ---: | ---: | ---: |
| 0 | 241.05 ยฑ 0.75 | **32.53 ยฑ 0.08** | **+36.7%** |
| 8192 | 220.14 ยฑ 1.73 | **29.19 ยฑ 0.67** | **+26.9%** |
| 32768 | 177.29 ยฑ 1.66 | **28.64 ยฑ 0.14** | **+31.9%** |

**Q2 on one node beats FAST on two at decode at every depth**, and loses at prefill at
every depth. Its decode curve is also flatter past 8k (29.19 โ†’ 28.64, โˆ’1.9% over a 4ร—
context increase) โ€” same 9-full/39-SWA attention structure as the 4-bit variants, with
less weight traffic per token on top.

Pick by workload: **long prompts / short answers โ†’ two-node FAST** (prefill wins).
**Short prompts / long answers, or interactive chat โ†’ single-node Q2** (decode wins,
and on one machine).

### Aggregate throughput under concurrency

Total tokens/s across all streams, 16 distinct prompts so that slots cannot share
a prefix-cache hit and inflate the result (`--parallel 8`, `-c 32768`).

| variant | C4 | C6 | C8 |
| --- | ---: | ---: | ---: |
| **`Q4_0_ROCMFP4_FAST`** (dpm=high) | **33.20 t/s** | **34.51 t/s** | 33.57 t/s |
| `Q4_0_ROCMFP4_FAST` (dpm=auto) | 31.93 t/s | 32.61 t/s | 33.92 t/s |
| `Q4_0_ROCMFP4` (dpm=auto) | 24.96 t/s | 26.37 t/s | 28.32 t/s |
| _(reference)_ `UD-Q4_K_XL` (dpm=auto) | 25.81 t/s | 29.19 t/s | 31.72 t/s |

**Aggregate throughput plateaus around 33โ€“35 t/s** and the `dpm=high` tuning barely
helps here โ€” under concurrency the GPU is already busy enough that the governor
ramps on its own. That is the mirror image of the single-stream case, where forcing
`high` is worth 14%.

`ROCmFP4_FAST` is fastest at every concurrency level, but **its margin over
`UD-Q4_K_XL` shrinks as concurrency rises** โ€” the FAST layout's advantage is in
memory bandwidth and dequant cost, which dominate single-stream decode; as batch
size grows the workload shifts toward expert scatter and compute, where the quants
converge.

MiMo-V2.5 is a 256-expert top-8 MoE, the least favourable case for batch
amortization: the number of distinct experts touched at batch B grows as
`256ยท(1โˆ’(1โˆ’8/256)^B)`, so more expert weights must be read as concurrency rises
instead of being amortized across the batch. This is why aggregate throughput
scales so weakly โ€” `ROCmFP4_FAST` gains only 6% going from 4 streams to 8, and
per-stream latency roughly halves over that range (7.98 โ†’ 4.24 t/s).

## Usage

### Single node โ€” `Q2_0_ROCMFPX` (fastest decode)

```bash
GGML_CUDA_ENABLE_UNIFIED_MEMORY=1 \
llama-server -m Q2_0_ROCMFPX-COHERENT/MiMo-V2.5-Q2_0_ROCMFPX-COHERENT-00001-of-00003.gguf \
    -ngl 999 -fa 1 --no-mmap -ctk f16 -ctv f16 \
    -c 32768 --parallel 8 --jinja --host 0.0.0.0 \
    --temp 1.0 --top-p 0.95 --min-p 0.0 --repeat-penalty 1.05
```

No `--rpc`, no `-dev`. `GGML_CUDA_ENABLE_UNIFIED_MEMORY=1` is required โ€” 90.92 GiB
exceeds the hard VRAM cap even though it fits the 128 GB unified pool.

### Two nodes โ€” `Q4_0_ROCMFP4_FAST` (best quality)

```bash
# node1
rpc-server -H 0.0.0.0 -p 50052

# node2
llama-server -m ROCmFP4-FAST/MiMo-V2.5-ROCmFP4-FAST-00001-of-00004.gguf \
    --rpc 192.168.2.1:50052 -dev ROCm0,RPC0 \
    -ngl 999 -fa 1 --no-mmap -ctk f16 -ctv f16 \
    -c 32768 --parallel 8 --jinja --host 0.0.0.0 \
    --temp 1.0 --top-p 0.95 --min-p 0.0 --repeat-penalty 1.05
```

Point llama.cpp at the **first shard**; it pulls in the rest automatically.

### Sampling โ€” use the model's own recommendation

Xiaomi's model card specifies **`temperature=1.0`, `top_p=0.95`** for local
deployment. `--repeat-penalty 1.05` and `--min-p 0.0` come from the community
configuration in [llama.cpp #23074](https://github.com/ggml-org/llama.cpp/issues/23074).

Use `--jinja` with the model's built-in chat template. No corrected template exists
or is needed โ€” neither Unsloth nor bartowski overrides it. You may see
`Expected iterable or object type in for loop: got None` in the log during
reasoning-format detection; it is harmless and also appears on runs that work.

**Note a MiMo-level quirk, not a quantization artifact:** MiMo-V2.5 can enter runaway
chain-of-thought โ€” "extremely long CoT (sometimes for hundreds of thousands of tokens)
with no progress or tool calls" (#23074), reproduced on **Q8_0**, so it is not caused
by low-bit quantization. Cap it with `--reasoning-budget`, and pass
`"chat_template_kwargs": {"enable_thinking": false}` per-request to disable thinking
entirely. Thinking costs ~0.5% of decode *rate* โ€” its real cost is the extra tokens.

Mind the flag spelling โ€” `llama-server` takes `--rpc` and comma-separated `-dev`,
while `llama-bench` takes `-rpc` and **slash**-separated `-dev` (`ROCm0/RPC0`).
Passing the wrong one makes the server exit immediately with
`error: invalid argument`.

### Things worth knowing

- **Keep KV cache at f16 for speed.** Measured on this exact model and split:

  | `-ctk` / `-ctv` | tg128 @d0 | tg128 @32k |
  | --- | ---: | ---: |
  | **f16 / f16** | **20.71** | **19.44** |
  | q8_0 / q8_0 | 20.26 | 17.13 |
  | q8_0 / q4_0 | 20.17 | 17.24 |
  | q4_0 / q4_0 | 20.04 | 17.22 |

  The penalty is ~2% at zero depth but **13% at 32k** โ€” ROCm dequant costs more
  than the bandwidth it saves, and the gap widens as the cache fills. Note the
  three quantized configs are indistinguishable: the cost comes from quantizing
  at all, not from how aggressively.

  **But quantizing buys context.** f16 KV is 22.5 KiB/token here; `q4_0/q4_0` is
  ~6.6 KiB โ€” about **3.4ร— the KV pool for that 13%**. If maximum context matters
  more to you than decode speed, `-ctk q4_0 -ctv q4_0` is the trade.

- **Prefer the USB4/Thunderbolt link over ethernet for the RPC hop.** Against a
  switched 1 GbE path we measured โˆ’5% decode and โˆ’9.5% prefill. The causes differ:
  decode is latency-bound (llama.cpp RPC does ~3โ€“4 round trips per token, so hop
  latency multiplies), while prefill is bandwidth-bound (4 MB of activations per
  512-token chunk). A *switched* 10 GbE link fixes the prefill half but not the
  decode half; a direct point-to-point cable fixes both.
- **Speculative decoding does not pay off on this model.** Measured on this exact
  build and split, with `--temp 0 --repeat-penalty 1.0`:

  | workload | no speculation | DFlash n=1 | DFlash n=2 | DFlash n=4 |
  | --- | ---: | ---: | ---: | ---: |
  | structured JSON | **21.00** | 14.87 | 13.80 | 10.02 |
  | Rust code | **21.18** | 13.00 | 11.96 | 8.94 |
  | narrative prose | **21.21** | 12.19 | 10.72 | 8.16 |

  DFlash costs 30โ€“60%, and gets worse the deeper you draft. **The cause is
  acceptance.** Mean accept length is 1.52 / 1.32 / 1.23 (JSON / code / prose),
  and per-position acceptance on JSON runs 0.500, 0.150, 0.011, 0.000 โ€” the
  drafter is right about half the time on token 1 and essentially never by token 3.

  A DFlash step costs **2.15ร—** a normal decode step here, so break-even needs
  accept length โ‰ฅ 2.15. Reference implementations on other engines report 3.78 on
  this same model and drafter, which would be ~1.76ร— โ€” so the headroom is real,
  it is just not reachable from this engine. An independent DFlash draft GGUF for
  MiMo-V2.5-Pro on `ik_llama.cpp` reports the same shape (54.6โ€“60.4% acceptance,
  55.6โ€“59.4 t/s drafted vs 59.9โ€“60.8 undrafted โ€” also a net loss).

  Measured with **thinking off**; enabling it changes JSON and prose by ~0 and
  costs code about 16%.

  MTP is unavailable (llama.cpp issue #23924 closed `not_planned`, though these
  GGUFs do carry the `blk.48-50.nextn.*` tensors), and no EAGLE3 drafter has been
  published for MiMo-V2.5.

- **If you do experiment with DFlash over an RPC split**, the target's LM head and
  token embeddings must be pinned to the local device or it aborts at load in
  `ggml_backend_sched_backend_id_from_cur`:
  `-ot "output\.weight=ROCm0" -ot "token_embd\.weight=ROCm0" -devd ROCm0`
- KV geometry: MiMo-V2.5 has 9 full-attention + 39 sliding-window layers, so
  only the 9 full layers scale with context โ€” about **22.5 KiB/token**.

## Speculative decoding (MTP) โ€” present, but do not expect a win

MiMo-V2.5 ships its own **MTP / NextN head** (`model_mtp.safetensors` upstream โ†’
`blk.48/49/50.nextn.*` in GGUF, 3 NextN layers on top of 48 trunk layers). All files
here preserve those tensors, so they are available if your build can use them.

**Using them requires engine support that is not yet in any release.**
[llama.cpp PR #26228](https://github.com/ggml-org/llama.cpp/pull/26228) adds `mimo2`
MTP draft support and is still open; the ROCmFPX fork does not carry it either. Without
it the nextn tensors are simply ignored (harmless โ€” the files still load and run
normally on a stock build; verified).

Two warnings, both measured on this hardware, because the intuitive expectations are
wrong in both directions:

**1. Across a two-node RPC split, MTP is a 33% *loss* โ€” even at 100% acceptance.**

| 2-node `ROCmFP4_FAST`, `-dev RPC0,ROCm0` | decode | acceptance |
| --- | ---: | --- |
| no MTP (control) | **23.55 t/s** | โ€” |
| MTP, `n-max 1` | 15.87 t/s | **1.000** (27/27) |

Acceptance was *perfect* and it still lost a third of throughput. The deficit is
per-draft-step cross-device synchronisation, not draft quality โ€” which means **no
better drafter can fix it.** EAGLE3 would be worse still: it needs three hidden-state
taps from layers spread across the trunk, so on a layer split some taps are always
remote, whereas MTP needs only the final pre-norm hidden state.

If you do try it, note the load-time crash and its fix: with `-dev ROCm0,RPC0` the
trailing nextn blocks land on the *remote* node, and reading the hidden state back
aborts inside `ggml_backend_rpc_buffer_get_tensor` during startup. **Flip the device
order to `-dev RPC0,ROCm0`** โ€” layers are assigned to `-dev` entries in order, so the
last device named gets the trailing blocks. `-ot` moves *weights* but **not** the KV
cache; pinning weights alone does not fix it.

**2. On a single node, whether MTP helps depends on the quant.**

| single node | base | best MTP | verdict |
| --- | ---: | ---: | --- |
| `IQ2_XXS` (third-party, 2.06 bpw) | 20.75 t/s | **25.81 t/s** (+24%) | worth it |
| `Q2_0_ROCMFPX` (this repo) | **35.57 t/s** | 31.50 t/s (โˆ’11%) | not worth it |

Two compounding reasons MTP loses on `Q2_0_ROCMFPX`: the base is already fast, so
there is less fixed overhead to hide, and **the nextn head is itself quantized to
2.5 bpw** โ€” mean acceptance length falls to 1.39โ€“1.75 here versus 1.95 on `IQ2_XXS`.
Lifting `token_embd`/`output` to Q6_K does not help the drafter; the nextn tensors
stay at 2.5 bpw.

Two counter-intuitive tuning notes if you experiment: **higher `--spec-draft-p-min`
is better** (0.75 โ†’ 31.50 vs 0.10 โ†’ 30.26; fewer, more confident drafts waste less
verify work), and **`--spec-draft-n-max 1` is optimal** โ€” depth โ‰ฅ2 loses badly
(22.19 t/s at n=2). Deeper drafts widen the verify batch, and on a 256-expert top-8
MoE a wider batch activates *more experts*, so weight traffic grows faster than
accepted tokens. Mean acceptance length keeps rising with depth while throughput
falls, so acceptance alone is a misleading metric here.

## License

Inherits the license of the base model,
[XiaomiMiMo/MiMo-V2.5](https://huggingface.co/XiaomiMiMo/MiMo-V2.5). Quantization
adds no additional restrictions.

## Credits

- [XiaomiMiMo](https://huggingface.co/XiaomiMiMo) โ€” MiMo-V2.5
- [unsloth](https://huggingface.co/unsloth) โ€” BF16 GGUF conversion used as the source
- [ROCmFPX](https://github.com/charlie12345/ROCmFPX) โ€” the ROCmFP4 quant formats