Repository navigation
cpu kernels: gathered decode on the cores where it is faster, AVX-VNNI rows, IQ3_S one-token kernel - #863
Closed
Hardin22 wants to merge 3 commits into
Closed
cpu kernels: gathered decode on the cores where it is faster, AVX-VNNI rows, IQ3_S one-token kernel#863Hardin22 wants to merge 3 commits into
Hardin22 wants to merge 3 commits into
Conversation
…re it is faster (STRATA_IQ256_GATHER auto) dd24a55 reads the IQ3 grid words with one gather under STRATA_IQ256_GATHER=1, on every core, and keeps it off by default because AVX2 gather throughput depends on the core. This makes the gathered decode its own Fmt32 (118, 121, 122), chosen once per call instead of a branch in every decode, and takes it further: - IQ3_XXS: the four even-sign masks by one 64-bit gather as well; - IQ3_S: the 9th index bit by one sllv and one and; the sign bytes by one broadcast and one shuffle; - IQ2_S: the four 10-bit grid indices built in a register and read by one 64-bit gather, the signs as IQ3_S's. Each is its format's scalar decode bit for bit: the same words land in the same lanes. IQ2_XXS measured 4% slower gathered and IQ2_XS has its own sign decode, so both keep the scalar one. STRATA_IQ256_GATHER: unset = auto, 0 = the scalar decode everywhere (as before), 1 = the gathered one everywhere. Auto gathers on a thread whose core is an Intel performance core of Alder Lake / Sapphire Rapids or newer: - GenuineIntel with AVX-VNNI (CPUID 7.1:EAX[4]) as the generation marker. That leaves out Haswell/Broadwell (slow gathers) and Skylake through Ice Lake / Tiger Lake (gathers slowed by the Downfall/GDS microcode); - not the E-core-only parts, which are not hybrid: Alder Lake-N / Twin Lake, Grand Ridge, Sierra Forest and Clearwater Forest, by model; - on a hybrid CPU only a thread on a P-core (CPUID 1Ah core type 40h): the E-cores gather slower than they assemble. AMD keeps the scalar decode (Zen 2/3 gather slowly; Zen 4/5 take the AVX-512 kernels). STRATA_FORCE_ISA turns auto off. The probes are in expert_layout.cpp, which is not compiled for AVX2, and the core type is asked once per thread (the pool pins its workers). Measured on an i9-14900KF, gate/up rows of one expert at three tokens on one P-core, scalar -> gathered: IQ3_S 0.40 -> 0.27 ms, IQ2_S 0.31 -> 0.27, IQ3_XXS 0.26 -> 0.24. On its E-cores the gathered decode was slower. iq_avx2.cpp has no runtime initializer again (016ea2e): the switch and STRATA_IQ_PREFETCH are read on first use. iq_avx2_parity, a CPU-only CTest: random blocks (every grid index and sign pattern occurs) for IQ2_XXS, IQ2_XS, IQ2_S, IQ3_XXS, IQ3_S and IQ4_XS gate/up rows and IQ4_NL / Q2_0 down rows; every variant, 1..8 tokens, each token's rows bit for bit the scalar variant's rows for that token alone; the scalar rows against ggml-cpu's vec_dot (Q2_0: a double reference). --bench: ms per expert on one thread, each variant, ggml's per-token dot and the engine's dispatch, over more expert blobs than an L3 holds.
…s (cpu_avxvnni_ok)
Alder Lake, Sapphire Rapids and later have AVX-VNNI on every core: vpdpwssd does the madd and the add of an i-quant
row in one uop, and vpdpbusd the maddubs + madd(ones) of the IQ4_NL and Q2_0 down rows. The sums are integer, so the
results are bit for bit those of the AVX2 forms: vpdpwssd wraps like madd + add, and the maddubs it replaces in the
down rows cannot saturate (IQ4_NL |w| <= 127, Q2_0 codes 0..3).
The row kernels move to iq_avx2_rows.inl and q2_avx2_rows.inl, which iq_avx2.cpp and q2_avx2.cpp include twice: once
as they were (namespace plain) and once with AVX-VNNI (namespace vnni). In the second copy every function is declared
with target("avxvnni") under GCC/Clang, so the intrinsics compile there and nowhere else: neither file gets
-mavxvnni, which would let the compiler use the extension in the plain path. MSVC takes the intrinsics under
/arch:AVX2. CMake checks that the compiler has them (GCC 11, Clang 12) and otherwise builds without the copies
(STRATA_AVXVNNI=0).
cpu_avxvnni_ok() in expert_layout.cpp, which is not compiled for AVX2: cpu_avx2_ok() and CPUID 7.1:EAX[4], off under
STRATA_FORCE_ISA (a CPU that stops at AVX2), off in a STRATA_ISA_FLOOR build, and STRATA_NO_AVXVNNI=1 turns it off.
The VNNI copies are reached only through the AVX2 kernels' entry points, which the engine already calls only behind
cpu_avx2_ok().
Measured on an i9-14900KF on top of the gathered decode: another 4-10% on a P-core's i-quant gate/up rows, 15-25% on
an E-core's Q2_0 down rows.
iq_avx2_parity runs the VNNI variants as well (iq4nl256_down_rows_v, q2_0_gguf_rows_multi_avx2_v): every combination
bit for bit against the scalar one-token rows, 1..8 tokens.
…its grid is gathered Niko1221#152's rule gives a lone token's gate/up rows to ggml's dot, because the multi-token kernels were no faster at one token. With the gathered decode that no longer holds for IQ3_S on the Intel cores that gather it: an expert's gate/up rows for one token on an i9-14900KF P-core take 0.34 ms with ggml's dot and 0.19 ms with the AVX-2 kernel. And in decode most CPU experts serve one token of the window. So on a CPU without AVX-512 where cpu_gather_fast() holds (Intel, Alder Lake or later, see the gather commit) and STRATA_IQ256_GATHER is not 0, IQ3_S takes the multi-token kernel from one token on. This only pays together with the gathered decode: on the same CPU's E-cores, which keep the scalar decode, it is 0.71 -> 0.68 ms, about even. That is why it follows the gather's machine-wide rule and not the per-thread one. Measured in the engine with Swift 1.5 IQ3_XXS (whose IQ3_S layers this reaches): the CPU expert time per verify window -4%. This changes results. A lone token's IQ3_S gate/up rows now round like the multi-token kernel's (the order of the float additions), no longer like ggml's dot. They are the same rows the token gets in a group, so on these CPUs IQ3_S experts no longer depend on how many drafts a window held (Niko1221#152), and every core rounds them alike. Down rows and the other formats keep the default rule. STRATA_IQ_MT_MIN set (2 is the old default) keeps its rule for every format. The decision is native_gu_mt_min(): every probe is in expert_layout.cpp and cpu_avx2_ok() comes first. Nothing compiled for AVX2 runs before that check, including on an older-CPU build (STRATA_ISA_FLOOR) on an AVX-only CPU. iq_avx2_parity checks native_gu_rows wherever native_gu_mt_min is 1: each token's rows must match alone and in groups of 2..8. docs/DETAILS.md notes the new default next to STRATA_IQ_MT_MIN.
txiki739
added a commit
to txiki739/Strata3090-5060TI
that referenced
this pull request
Oct 5, 2026
…is tree) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
txiki739
added a commit
to txiki739/Strata3090-5060TI
that referenced
this pull request
Oct 5, 2026
…1221#838 q8_1 finite, Niko1221#863, Niko1221#851, Niko1221#591) and what they gave
Niko1221
added a commit
that referenced
this pull request
Oct 6, 2026
…T1=1); the default keeps 0.1.39's rounding Items 1-2 of #863 (gathered decode per core, AVX-VNNI rows) are bitwise and stay. Item 3 changes a lone token's IQ3_S rounding on Intel hybrid CPUs, so it needs the switch. The #706 bit-plane path stays in front of the VNNI/plain rows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner
|
In 0.1.40: items 1 and 2 (gathered decode per core, AVX-VNNI rows) are in. Item 3 (IQ3_S one-token kernel) is opt-in as STRATA_IQ3S_MT1=1, since it changes a lone token's rounding. A GCC/Clang build log and an end-to-end IQ3_S result on an Intel hybrid CPU are still welcome. |
fragtion
added a commit
to fragtion/StrataPool
that referenced
this pull request
Oct 6, 2026
…/ IQ2_S decode on the cores where it is faster (STRATA_IQ256_GATHER auto) (merged with Niko1221#851's q8k_quant_avx2 in the same files)
fragtion
added a commit
to fragtion/StrataPool
that referenced
this pull request
Oct 6, 2026
Upstream merged Niko1221#863, Niko1221#732, Niko1221#869, Niko1221#931 and Niko1221#846 (our copies dropped) and took its own form of Niko1221#970 (Niko1221#804). Kept: Niko1221#851, Niko1221#652, Niko1221#855, Niko1221#813, Niko1221#789. A pool turns off what it cannot run yet: --pipeline-windows, --batch-mtp, --kv-grow, session files. 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 6, 2026
…/ IQ2_S decode on the cores where it is faster (STRATA_IQ256_GATHER auto) (merged with Niko1221#851's q8k_quant_avx2 in the same files)
txiki739
added a commit
to txiki739/Strata3090-5060TI
that referenced
this pull request
Oct 6, 2026
…iant knobs (opt-in) Port of Niko1221#930 (sanastasiou) onto the gathered-variant layout of Niko1221#863: the scalar IQ3_S and IQ2_S decodes take their grid indices from a constexpr high-bit table (one punpcklbw instead of a shift, mask and or per index), their sign vectors from one broadcast load and one pshufb, and their (2s+1) scales from constexpr tables. Bit-identical: iq_avx2_parity passes (every variant, 1..8 tokens, against ggml-cpu). On a Ryzen 7 5700X (one thread, IQ3_S gate/up rows, ms an expert) the scalar decode goes 0.403 -> 0.278 at one token, 0.393 -> 0.370 at three; the gathered decode (Fmt32<121>, unchanged) stays ahead at 2, 4 and 5 tokens. Two opt-in knobs to measure the mix (unset: the behaviour so far): - STRATA_IQ256_GATHER_NT=2,4,5: the gathered decode only for those token counts; - STRATA_IQ_DOWN_MT_MIN: the IQ4_NL down rows' multi-token rule apart from STRATA_IQ_MT_MIN (ggml's one-token dot is faster for down on this CPU). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anon761
pushed a commit
to anon761/Strata-upstream
that referenced
this pull request
Oct 6, 2026
…T1=1); the default keeps 0.1.39's rounding Items 1-2 of Niko1221#863 (gathered decode per core, AVX-VNNI rows) are bitwise and stay. Item 3 changes a lone token's IQ3_S rounding on Intel hybrid CPUs, so it needs the switch. The Niko1221#706 bit-plane path stays in front of the VNNI/plain rows.
sskver
pushed a commit
to sskver/Strata
that referenced
this pull request
Oct 7, 2026
…T1=1); the default keeps 0.1.39's rounding Items 1-2 of Niko1221#863 (gathered decode per core, AVX-VNNI rows) are bitwise and stay. Item 3 changes a lone token's IQ3_S rounding on Intel hybrid CPUs, so it needs the switch. The Niko1221#706 bit-plane path stays in front of the VNNI/plain rows.
eddoursul
pushed a commit
to eddoursul/Strata
that referenced
this pull request
Oct 12, 2026
…is tree) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three changes to the AVX2 expert kernels. Each one gives the same bits as the scalar path, except (c), which follows the #152 rule.
STRATA_IQ256_GATHER: unset = auto, 0 = off, 1 = every core).vpdpwssd/vpdpbusdforms of the i-quant, IQ4_NL and Q2_0 rows. Integer sums, so the same bits.cpu_avxvnni_ok(), which lives inexpert_layout.cpp(not compiled for AVX2) and respectsSTRATA_FORCE_ISAandSTRATA_ISA_FLOOR.STRATA_NO_AVXVNNI=1turns it off.target("avxvnni"), and only if CMake finds the intrinsics. No file gets-mavxvnni.STRATA_IQ_MT_MINkeeps its meaning.Test:
iq_avx2_parity, a CPU-only CTest. Random blocks for IQ2_XXS, IQ2_XS, IQ2_S, IQ3_XXS, IQ3_S and IQ4_XS gate/up rows and IQ4_NL / Q2_0 down rows. Every variant at 1..8 tokens must match the scalar variant bit for bit; the scalar rows are checked against ggml's vec_dot (Q2_0 against a double reference).STRATA_IQ256_GATHER=0,STRATA_IQ_MT_MIN=1,STRATA_NO_AVXVNNI=1andSTRATA_FORCE_ISA=avx2, and pinned to a P-core and to an E-core.STRATA_FORCE_ISA=avxit skips cleanly.Measured (
iq_avx2_parity --bench --dispatch, i9-14900KF, one thread, more expert blobs than L3, median of 5)Engine path, ms per expert (gate/up + down) at 1-4 tokens. Upstream main is shown at its default (
STRATA_IQ256_GATHERunset = 0) and with=1:The full grid has 5 formats × 1-4 tokens × P/E core. Nowhere is it slower than main beyond noise. On E-cores,
gather=1is 40-75% slower, which is why auto leaves them alone.End to end: Swift 1.5 IQ3_XXS on an RTX 5080 alone (most of the window is CPU experts) showed no difference beyond noise. That is expected: its CPU experts mostly see one token, and IQ3_XXS at one token is unchanged. I don't have an IQ3_S model here, so the end-to-end effect on IQ3_S, where one token goes from 0.43 to 0.26 ms per expert, is not measured. A report from an IQ3_S rig would settle it.
Not tested
target("avxvnni")copies and the CMake check have never been compiled. If the check fails, the build simply leaves the VNNI copies out.