Repository navigation
Conversation
|
Hi @ptweezy! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
ZSTD_cpuSupportsBmi2() runs CPUID on every CCtx and DCtx init, including each one-shot ZSTD_compress() and ZSTD_decompress() call. In a VM, each CPUID traps to the hypervisor: about 420 ns per instruction under WSL2 on a Ryzen 7 9800X3D, and about 2 us according to meta#4746. For small one-shot calls, that is a large share of the total time. The result cannot change while the process runs, so cache it in a function-local static, read and written with relaxed __atomic builtins. Without __atomic, the function falls back to the uncached probe. Kernel, MSVC, non-x86, and -mbmi2 builds never call this function.
947f09f to
4cc9e76
Compare
What and why
ZSTD_cpuSupportsBmi2()runs CPUID on every CCtx and DCtx init, and each one-shotZSTD_compress()andZSTD_decompress()call does an init. In a VM, CPUID traps to the hypervisor and costs about 2 µs instead of about 100 ns (#4746); on the WSL2 machine below it costs about 420 ns. Callers that create a context per message pay this on every message: librdkafka callsZSTD_decompress()and creates a CStream per batch, and systemd creates a DCtx per journal field it decompresses.This change caches the result in a function-local
static intthat starts at -1. Loads and stores use relaxed__atomicbuiltins, so concurrent first calls are race-free; every thread stores the same value. Without__atomic, the function falls back to the uncached probe. That only matters ifDYNAMIC_BMI2is forced on for another compiler; by default it requires GCC ≥ 4.8 or Clang, which both have__atomic. The static is per translation unit, so a normal build keeps two caches per process, one inzstd_compress.cand one inzstd_decompress.c.-mbmi2, kernel, MSVC, and non-x86 builds never call this function.Related: #4746 and #4767 removed the probe for
-mbmi2and kernel builds, respectively. #4888 (open) caches its new APX probe the same way and does not touch the BMI2 probe. The two PRs edit adjacent lines ofzstd_internal.h, so whichever merges second needs a trivial rebase.Performance
DYNAMIC_BMI2is 0, so the probe never runs.lib/libzstd.afromdevand from this branch, built with default flags (make -C lib libzstd.a). Bench: built with-O2, source below.x86-64, ns per call:
Context create+free saves about 860 ns, the cost of two CPUIDs. One-shot and new-DCtx calls up to 4 KiB save 0.8 to 1.4 µs. Calls on a reused context do not change. On arm64, every row is within 1% of
dev.Not measured: bare-metal x86-64, Intel CPUs, and cloud VMs.
Bench source (ctx_ab.c)
Testing
make checkandmake -C tests test-fuzzer test-zstream(90 s each): pass on x86-64 (WSL2, GCC 13.3.0) and arm64 (macOS, Apple clang 21).make tsan-test-zstream: passes on x86-64. It ran undersetarch x86_64 -Rbecause GCC 13's libtsan fails at startup with this WSL kernel's ASLR settings.devand this branch for 544 frames: sizes 0 B to 1 MiB, levels -5 to 19, one-shot and streaming APIs. The digest is the same on x86-64 and arm64.-fsanitize=thread, 8 threads racing on the first context creation produce no reports in 11 runs.-arch x86_64,lib/compiles with-Werror, with thec89buildflags, and as C++ (the last two with asm off).The x86-64 runs used the same change on 49cf517. Since then,
devhas changed only CI files andprograms/zstdcli.c. On arm64,make checkalso passes on this branch as submitted.Checklist
A gain in one configuration is not a general gain: see the Performance section of CONTRIBUTING.md.
Tools used
Claude was used to help write the change, the benchmark, and the correctness checks.