Pull requests / #3472
#3472 [Engine] Zen 1/2: build ggml-cpu without BMI2 pdep (IQ1_S/IQ1_M CPU dots 16-19x faster, same results)
open · @Licenser · 0 Kommentare · Auf GitHub
Beschreibung
## Problem and result On AMD Zen 1, Zen+ and Zen 2 (CPU family 17h, and Hygon's 18h) the BMI2 instruction `pdep` runs in microcode: on the order of a hundred cycles or more, against a few on Intel and Zen 3+. ggml-cpu's AVX2 IQ1_S and IQ1_M dot products build their grid indices with `pdep` whenever `__BMI2__` is defined, which every `-march=native` build on such a CPU does. There those two dots run 10-19x slower than the `#else` branch beside them, which computes the same integer sums with shifts and masks. Upstream GSQ-RCO IQ2_XS has IQ1_M gate/up experts in 3 of its 48 layers (8, 13, 37), so on a Zen 2 PC those three layers take most of the CPU pool's time. Measured on a Threadripper 3960X (Zen 2) + RTX 2080 Ti 11 GB, Linux, GCC 15.2, upstream IQ2_XS: | | with pdep | without | |---|---|---| | `ggml_dot_bench`, IQ1_S, ns per 256 weights (one thread) | 379 | 24 | | `ggml_dot_bench`, IQ1_M, ns per 256 weights | 543 | 29 | | `kq_cpu_bench`, one IQ1_M expert, ms per thread (24 threads, 1 / 2 tokens) | 7.53 / 14.99 | 0.72 / 1.42 | | engine, CPU time per CPU expert | 46-51 us | 21-23 us | | engine, decode tok/s (4 requests) | 58-76 | 71-99 | | engine, short prompts tok/s | 58-70 | 94-103 | | engine, 6.3K-token prompt tok/s (runs on the GPU) | 1104 | 1115 | The output does not change: both builds compute byte-identical results (Validation). After this change, a native build on a Zen 1/2 build machine compiles ggml-cpu without `__BMI2__`, so the two dots take their existing `#else` branch. Nothing changes anywhere else. ## Related discussion None. This only changes what a native build compiles on Zen 1/2 build machines, and the results stay identical, so I treated it as a focused performance fix. Happy to open an issue first if you would rather discuss the default. ## Approach A new CMake option, `STRATA_GGML_PDEP` (`AUTO` / `ON` / `OFF`, default `AUTO`): - `AUTO`: in a native build (not `STRATA_PORTABLE`, no `STRATA_ISA_FLOOR`) whose build machine is AMD family 17h or Hygon 18h (`/proc/cpuinfo`, or `PROCESSOR_IDENTIFIER` on Windows), sets `GGML_BMI2` off and adds `-U__BMI2__` to ggml-cpu (GCC and Clang: `-march=native` defines `__BMI2__` whatever `GGML_BMI2` says). CMake prints a status line when this applies. - `OFF`: no `pdep` in any build. `ON`: never removed. - Unchanged: the portable (release) build, which also runs on CPUs where `pdep` is fast; every other CPU; MSVC builds, which define `__BMI2__` only through `GGML_BMI2` (set only by the release build), so they already used the `#else` branch. No ggml source is touched; the `#else` branches are ggml's own code. Besides `ggml_cpu_has_bmi2()` (a report), these two dots are ggml's only `__BMI2__` code. Also adds `ggml_dot_bench` (ggml-cpu's dot product per format, with a checksum of the results, CPU only, no model) and `tools/bench_ggml_pdep.py`, which builds it with and without `pdep` and prints both side by side, so other PCs can measure. `docs/DETAILS.md` documents the option. ## Validation **Passed:** - `python tools/bench_ggml_pdep.py --passes 2` (this commit; 3960X, Linux, GCC 15.2): IQ1_S 379.3 -> 23.8 ns, IQ1_M 543.0 -> 28.6 ns per 256 weights. The 9 other formats (IQ2_XXS, IQ2_XS, IQ2_S, IQ3_XXS, IQ3_S, IQ4_NL, IQ4_XS, Q2_K, Q4_K) stay within 0.93-1.08x, and the checksums of all 11 formats are equal between the two builds. - `objdump` of the engine built from this commit (CUDA sm_75, `AUTO` on the 3960X): 0 `pdep`. Its two IQ1 dot functions are instruction-for-instruction those of the `-U__BMI2__` engine used for the measurements below. - `kq_cpu_bench --pairs iq1_m/q2_0,iq2_s/q2_0 --threads 1,24 --nt 1,2`: iq1_m/q2_0 7.53 -> 0.72 ms per expert per thread (24 threads, 1 token), 14.99 -> 1.42 (2 tokens). iq2_s/q2_0, which has no `pdep`: 0.554 vs 0.571 ms. - `tools/ab_engine.py`, 4 alternating rounds, upstream IQ2_XS, `--expert-cache auto`, `--spec 4` with the draft layer, `STRATA_DECODE_TIMING=1`: the engine rows of the table above (medians per request). Engines built from 477ac13c plus local changes that are off by default, once as is and once with `-DCMAKE_C_FLAGS=-U__BMI2__ -DCMAKE_CXX_FLAGS=-U__BMI2__`, which gives the same object code as this option (see `objdump` above). - `tools/quant_quality.py run` with a fixed placement (every expert on the CPU, no adaptation, so layers 8, 13 and 37 run the IQ1_M dot): the two builds' outputs (4,995 positions: the target's logp, the argmax and the 64 most likely tokens each) are byte-identical (sha256 245adafe...). - The engine with the draft window sizes held fixed (a local test switch, not part of this PR): both builds gave the same tokens and the same draft and cache counters; with that same work, decode was 19-31% faster and short prompts 48-60%. - Builds: CUDA (sm_75) and CPU-only, Linux, GCC 15.2, CUDA 13.4. **Failed:** - None observed. **Not run:** - Zen 1 / Zen+ and Hygon hardware: detected by CPU family, where `pdep` is also microcoded, but not measured. - Intel and Zen 3+: `AUTO` does not apply there. `tools/bench_ggml_pdep.py` should show both builds close. - The release (portable) build on Zen 2: unchanged by this PR. - HIP, SYCL and Metal builds; Windows (MSVC) builds, including the `PROCESSOR_IDENTIFIER` branch. ## Limits - The release build keeps `pdep`, so Zen 1/2 users of the release binaries still pay for it. Fixing that needs a runtime choice in ggml, which is out of scope here. - The choice follows the build machine: a native build copied to another CPU keeps it. - Only packs with IQ1_S / IQ1_M layers change speed (upstream IQ2_XS: 3 of 48 layers; the IQ3_S, IQ3_XXS and Q2_0 packs have none). - One machine measured. - In a normal engine A/B the two builds' answers can differ although the arithmetic is identical: the draft policy prices window sizes from measured round times, so a faster CPU picks other windows. With the windows held fixed they are the same. Disclaimer: I'm not a C++ dev / CMake is not a usual tool for me, this used Claude and I reviewed to the best of my ability
Mehr auf der Site
Links zu Install, Modellen, Releases.