Skip to content

experts: IQ1_S on GPU/AVX-2, IQ1_S+IQ1_M on AVX-512, and a measured PCIe share - #1374

Closed
Yxmura wants to merge 8 commits into
Niko1221:mainfrom
Yxmura:pr-iq1s-v2
Closed

Yxmura wants to merge 8 commits into
Niko1221:mainfrom
Yxmura:pr-iq1s-v2

Conversation

@Yxmura

@Yxmura Yxmura commented Oct 7, 2026 •

Copy link
Copy Markdown

Round 2: IQ1_S made first-class across the pipeline, plus a PM-driven PCIe share.

What

  • IQ1_S (type 19) on the GPU and on AVX-2. Unsloth UD-IQ1_S packs did not run at all before: dq_iq1_s, vec_dot_iq1_s_q8_1, Fmt<19>, Split<19>, the grouped kernels, the reader geometry, and the AVX-2 row_dot_iq1s/row_dot_iq1m.
  • IQ1_S + IQ1_M on 512-bit lanes. iq512_supported listed neither, and native_expert.cpp only takes the AVX-2 kernel off an AVX-512 CPU, so AVX-512 CPUs ran both through ggml-cpu single-token — slower than an AVX-2 CPU. No EVEX vpsignb, so the grid sign is vpabsb + a masked subtract.
  • IQ1_S on the MMQ prompt path. IQ1_S was absent from _strata_mmq_types, mmq::supported() and the dispatch switch, so UD-IQ1_S prefill fell back to per-expert dequant→FP16. Added; the mmq-instance-iq1_s.cu already exists in the pinned ggml.
  • The PCIe share measures itself (serve). --pcie-frac was the link probe alone (capped 0.55); the best share also depends on the CPU/GPU and runs both ways. A --serve sweeps the probed share, 0 and 1.0 over its first verify windows, keeps the lowest (CPU pool + GPU wait) per routed expert entry, and holds it. --pcie-frac <value> pins; --pcie-frac auto for the CLI.

Measured

Radeon PRO W7800 (gfx1100, ROCm 7.13), Ryzen 7 3800X, 30 GiB RAM; UD-IQ1_S (67.5 GiB); 600-token prompt, MTP.

before after
UD-IQ1_S loads/answers no yes
decode (this box) 26.5 tok/s (probed share) 40.2 → 75.3 tok/s (measured, held)
prefill 169.7 tok/s 326.8 tok/s (+93%: +16% the other formats, +66% from IQ1_S MMQ alone)
AVX-512 CPU, IQ1_S/IQ1_M gate/up ggml-cpu single-token multi-token AVX-512

The share runs both ways, which is why it is measured: here the GPU taking every miss is fastest, while in the RDNA2 x8 bench --pcie-frac 0 beat the probed 0.39.

Validation

  • HIP gfx1100 + full strata; iq_parity (IQ1_S/IQ1_M dequant rel 0.00e+00), iq_multi_parity, native_expert_parity (IQ1_S 3.1e-08, IQ1_M 4.1e-08 vs ggml-cpu), native_grouped_parity — 0 failures.
  • AVX-512 path under Intel SDE -spr (this Zen 2 cannot run it): rel ~1e-07 vs ggml-cpu, width-invariant, 0 failures.
  • hip_prefill_mmq_parity extended with a synthetic IQ1_S case: rel_l2 0.0038 (limit 0.04), max_abs/ref_rms 0.0145, 0 zero rows.

Yxmura added 3 commits October 7, 2026 16:01
Unsloth's UD-IQ1_S keeps the routed gate/up experts in IQ1_S (1.5625 bpw, 50 B per 256-value block), a
format no backend could run: the dequantizer, the MMVQ dot and the grouped expert kernels now cover it on the
GPU (Fmt<19>, Split<19>, dq_iq1_s, vec_dot_iq1_s_q8_1), and the AVX-2 multi-token kernels cover IQ1_S and
IQ1_M on AVX2 CPUs.  gguf_reader knows the block geometry, and the fixture/parity suites (iq_parity,
iq_multi_parity, native_grouped_parity) cover the new type.

Validated on a Radeon PRO W7800 (gfx1100, ROCm 7.13): iq_parity dequant 0.00e+00 against gguf-py; the MMVQ
and grouped kernels bitwise equal to the per-column and v1 kernels; native_expert_parity vs ggml 3.1e-08
(IQ1_S) / 4.1e-08 (IQ1_M).

Also adds the gfx1100 hipBLASLt 1.3.0 tuning table (3.6-18.2x vs plain hipBLASEx over the engine's 30 shapes,
calibrated on a W7800) and names the Radeon PRO W7900 / W7800 in the docs.
iq512_supported() did not list IQ1_S (19) or IQ1_M (29), and native_expert.cpp only takes the
AVX-2 kernel when the CPU has no AVX-512, so an AVX-512 CPU (Ice Lake+, Zen 4/5) ran these two
formats through ggml-cpu's single-token vec_dot - slower than the AVX-2 kernel an older CPU gets.

Port the AVX-2 row_dot_iq1s/row_dot_iq1m to 512-bit lanes (64 values per pass, the same grid
decode and qh delta as ggml's ggml_vec_dot_iq1_s_q8_K / _iq1_m_q8_K; only the float-addition order
differs), routed through row_dot_any.  AVX-512 has no EVEX vpsignb, so the grid sign is applied with
vpabsb + a masked subtract under the sign bits.

Validated with Intel SDE (sde -spr): native_expert_parity synthetic iq1_s and iq1_m, rel 1.0e-07
against ggml-cpu's vec_dot, width-invariant over 1..3 tokens, 0 failures.
The share of the VRAM misses the GPU reads over PCIe (`--pcie-frac`) was set from a host->device link
probe alone, capped at 0.55.  But the best share also depends on the CPU and the GPU, and it runs both
ways: on a Ryzen 7 3800X + Radeon PRO W7800 (x16, 28 GB/s) the GPU taking every miss measured 39-40 tok/s
against the probed 0.55's 26, while in the RDNA2 x8 bench `--pcie-frac 0` beat the probed 0.39.

A `--serve` now sweeps a few shares over its first verify windows - the probed one first, then 0 and 1.0 -
and keeps the one with the lowest (CPU pool + GPU wait) per routed expert entry (normalized so a window
that accepted more drafts is comparable; the min of a point's windows rejects a one-off spike).  The
winner holds for the rest of the process, so only the first request pays.  `--pcie-frac <value>` pins a
share and skips the sweep; `--pcie-frac auto` does the same for a CLI run.

Measured (600-token prompt, MTP, UD-IQ1_S on the W7800): serve 40.2 tok/s on the first request and 75.3 on
the second (persisted), against 26.5 at the probed 0.55.  iq_parity / iq_multi_parity / native_expert_parity
/ native_grouped_parity: 0 failures.
fragtion pushed a commit to fragtion/StrataPool that referenced this pull request Oct 7, 2026
…the GPU and on AVX-2, and IQ1_S / IQ1_M on 512-bit lanes

kernels: run IQ1_S (GGML type 19) on the GPU and on AVX-2

Unsloth's UD-IQ1_S keeps the routed gate/up experts in IQ1_S (1.5625 bpw, 50 B per 256-value block), a
format no backend could run: the dequantizer, the MMVQ dot and the grouped expert kernels now cover it on the
GPU (Fmt<19>, Split<19>, dq_iq1_s, vec_dot_iq1_s_q8_1), and the AVX-2 multi-token kernels cover IQ1_S and
IQ1_M on AVX2 CPUs.  gguf_reader knows the block geometry, and the fixture/parity suites (iq_parity,
iq_multi_parity, native_grouped_parity) cover the new type.

Validated on a Radeon PRO W7800 (gfx1100, ROCm 7.13): iq_parity dequant 0.00e+00 against gguf-py; the MMVQ
and grouped kernels bitwise equal to the per-column and v1 kernels; native_expert_parity vs ggml 3.1e-08
(IQ1_S) / 4.1e-08 (IQ1_M).

Also adds the gfx1100 hipBLASLt 1.3.0 tuning table (3.6-18.2x vs plain hipBLASEx over the engine's 30 shapes,
calibrated on a W7800) and names the Radeon PRO W7900 / W7800 in the docs.

kernels: run IQ1_S and IQ1_M on 512-bit lanes

iq512_supported() did not list IQ1_S (19) or IQ1_M (29), and native_expert.cpp only takes the
AVX-2 kernel when the CPU has no AVX-512, so an AVX-512 CPU (Ice Lake+, Zen 4/5) ran these two
formats through ggml-cpu's single-token vec_dot - slower than the AVX-2 kernel an older CPU gets.

Port the AVX-2 row_dot_iq1s/row_dot_iq1m to 512-bit lanes (64 values per pass, the same grid
decode and qh delta as ggml's ggml_vec_dot_iq1_s_q8_K / _iq1_m_q8_K; only the float-addition order
differs), routed through row_dot_any.  AVX-512 has no EVEX vpsignb, so the grid sign is applied with
vpabsb + a masked subtract under the sign bits.

Validated with Intel SDE (sde -spr): native_expert_parity synthetic iq1_s and iq1_m, rel 1.0e-07
against ggml-cpu's vec_dot, width-invariant over 1..3 tokens, 0 failures.

(The PR's third commit, the PCIe share measured over a --serve's first windows, is not taken: StrataPool's
calibrated configs pin --pcie-frac, and a pool worker's windows are not timed that way.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
Yxmura added 2 commits October 7, 2026 16:59
IQ1_S was missing from both _strata_mmq_types and mmq::supported(), and from the dispatch switch, so
Unsloth UD-IQ1_S packs fell off the MMQ prompt path entirely: every expert ran the per-expert
dequant-to-FP16 fallback (the RTX 5070/IQ3_S reference uses MMQ).  Add it; the iq1_s MMQ instance
already exists in the pinned ggml and force_float_ref is NULL, so a synthetic run feeds random valid
blocks (the check decodes them through ggml's to_float).

Measured on a Radeon PRO W7800 (ROCm 7.13, 600-token prompt, same build):
  MMQ off                          169.7 tok/s
  MMQ on, IQ1_S uncovered          196.4 tok/s   (+16%, the other formats)
  MMQ on, IQ1_S covered            326.8 tok/s   (+66% from IQ1_S alone)
hip_prefill_mmq_parity extended with a synthetic-IQ1_S-GU case: rel_l2 0.0038 (screen limit 0.04),
max_abs/ref_rms 0.0145, 0 zero rows.
Resolve the moved iq256_supported / iq512_supported functions (now in
native_expert.cpp on main): take main's deletion in iq_avx2.cpp and
iq_avx512.cpp, and port this branch's added types (IQ1_S=19, 29) into
native_expert.cpp.
@Yxmura

Yxmura commented Oct 9, 2026

Copy link
Copy Markdown
Author

Merged main in to clear the conflict (was CONFLICTING).

The conflict: main moved iq256_supported() / iq512_supported() out of iq_avx2.cpp / iq_avx512.cpp into native_expert.cpp (an ISA-free file, per #795). This branch had edited those functions in place to add IQ1_S (type 19) and type 29.

Resolution: take main's deletion in the two ISA files, and port the branch's added types into the relocated functions in native_expert.cpp:

  • iq512_supported: 16|17|18|19|21|22|29
  • iq256_supported: 16|17|18|19|21|22|23|29

Validated (ROCM 7.13, gfx1100, pinned ggml 3cf03257):

  • iq_parity, iq_multi_parity, native_grouped_parity: pass
  • hip_prefill_mmq_parity: pass, including the new synthetic-IQ1_S-GU case (type=19, rel_l2=0.00377, 0 zero rows)
  • full strata engine target builds

No other conflicts; the branch now sits on current main.

- generate.cpp: main's PcieAuto and the branch's `--pcie-frac auto` sweep both measure the
  share, so both stay, one driver at a time: an explicit `--pcie-frac auto` picks the sweep
  (and counts as a given share), PcieAuto otherwise when it is armed.
- iq_avx2.cpp / iq_avx512.cpp / native_expert.cpp: no new conflict; the earlier resolution
  (the predicates moved to native_expert.cpp, with IQ1_S 19 and IQ1_M 29) carried over.
@Yxmura

Yxmura commented Oct 12, 2026

Copy link
Copy Markdown
Author

Merged main (0.1.42) again; the branch is clean against it.

  • iq_avx2.cpp / iq_avx512.cpp / native_expert.cpp: the earlier resolution carried over, the predicates live in native_expert.cpp with IQ1_S (19) and IQ1_M (29).
  • generate.cpp: main's PcieAuto and this branch's --pcie-frac auto sweep both measure the share, so both stay, one driver at a time. An explicit --pcie-frac auto picks the sweep, PcieAuto otherwise when it is armed.

Built the merged tree (strata, HIP gfx1100) and ctest -R 'iq_parity|iq_multi_parity|native_grouped_parity': 4/4.

@Niko1221

Copy link
Copy Markdown
Owner

Thanks. The PR bundles three things: IQ1_S support, IQ1_S on the MMQ prefill path and a measured PCIe share that changes the default. We cannot validate IQ1_S here (no UD-IQ1_S pack and no gfx1100 card), and the PCIe part overlaps the share refinement already in the engine, so we are not taking it as a speed change. A PR with only the IQ1_S format support (kernels + reader + parity tests), rebased on the current main, is something we could review separately.

# Conflicts:
#	setup.py
#	src/program/generate.cpp
@Niko1221

Copy link
Copy Markdown
Owner

Thanks for the pull request.

We're resetting the tracker for 0.2.0-beta. There are more than 1,300 open issues and pull requests, too many for us to review properly, so we're closing everything that isn't being worked on right now. Closed here does not mean rejected.

To reopen it takes about two minutes:

  1. Rebase on main if it no longer applies, and re-run your checks on 0.2.0-beta.
  2. Start the title with its area: [Engine], [GUI], [TUI], [Benchmark], [Docs] or [Other].
  3. Fill in the pull request template: the problem and result, and the validation (what you ran, on which revision, what you saw).
  4. Comment /reopen. A check runs and either reopens the pull request or lists what is still missing.

The rules are in CONTRIBUTING.md.

@Niko1221 Niko1221 closed this Oct 19, 2026
fragtion added a commit to fragtion/StrataPool that referenced this pull request Oct 24, 2026
…ne 0.1.49.1) into pool-support

Conflicts: src/kernels/cpu/iq_avx2_rows.inl - upstream's own AVX2 IQ1_M rows (ggml's float order, pair sums once
per call, the wide prompt form) replace Niko1221#1374's IQ1_M; Niko1221#1374's IQ1_S (19) rows are kept beside them (non-exact
dispatch only; upstream has no IQ1_S), and iq256/iq512_supported still send Qwen's IQ1_S/IQ1_M experts to these rows.
CMakeLists.txt (0.1.49.1 after our pre-project MSVC flags), README.md (ours).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
fragtion added a commit to fragtion/StrataPool that referenced this pull request Oct 24, 2026
…bles; the raw results and the chart removed

The 2026-10-06 results (bench/results/2026-10-06-stratapool, 27 files) and docs/media/pool-results.svg described
StrataPool on Strata 0.1.40.1.  The README now gives the expected gains as ranges against the faster PC alone (one
chat +20-30%, two chats +25-30%, long prompts about a third slower, Several chats a loss on two PCs), says what
they were measured on, adds the long-session numbers from StrataPool on Strata 0.1.45 and the VRAM headroom lesson,
and points at tools/pool_bench.py for measuring a pool (its output folder, bench-results/, is already ignored).
Also: the "CPU help on short prompts" line and architectds' CPU-assist credit row are gone (Strata's own CPU prefill
share replaced it at 0.1.43, as the credits paragraph says), Niko1221#1374's row notes Strata 0.1.49's own AVX2 IQ1_M rows,
and DeepSeek-V4.1 is named as Share-requests-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
fragtion added a commit to fragtion/StrataPool that referenced this pull request Oct 27, 2026
…ool-support

Conflicts (14 files):
- generate.cpp (12): upstream took Niko1221#1284 (--adapt-decay checked); STRATA_PIPED_MTP (Niko1221#4121) beside the pool's
  batch-MTP rules (pmtp needs a local split, so never on a pool coordinator, whose last stage may be a worker:
  st_out/st_launch keep the remote stage, pmtp_verdict / batch_launch_rows only where pmtp is on), the pool worker's
  draft-in-groups refusal kept, the pool's 96 MiB beside nibble_max_slot_bytes, wide pool rows beside batch_max_t(),
  the invariant window pool function with pool_pipe, pool_conv_retain closed before ParkSource, the slot penalty
  windows in the non-pmtp group fill, the pool's batch-wait line and the local-stage profile loop.
- prefill.cpp: layer-major prefill (Niko1221#3976) declines on a pool coordinator or worker (remote_next / headless): its
  walk has no hand-off to other PCs yet.
- iq_avx512.cpp: upstream's Singleton rows beside Niko1221#1374's IQ1_S / IQ1_M 512-bit rows (row_dot_any forwards it);
  iq_kernels.cu: IQ1_S (19) beside the new TQP types 147-149.
- conversation_cache.hpp: pool_id beside dev_tag / pending; expert_cache.*: upstream's Niko1221#1911 and nibble code.
- serve/server.py: split serving (Niko1221#4000) in the plain (role off) branch beside the pool roles; #pool kept on the URL.
- serve/lifeline.py, strata_control/procs.py: upstream's own fix of the watcher token (start time only, /proc
  first) replaces ours.  index.html: the Pool tab beside the i18n tabs.  .gitignore both; CMakeLists 0.1.52;
  README ours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157fjixQD2nRxvCvtXq66DD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants