[AArch64] Optimize ZSTD_isRLE() with 64-byte NEON vector unrolling - #4736
[AArch64] Optimize ZSTD_isRLE() with 64-byte NEON vector unrolling#4736wittkung wants to merge 1 commit into
Conversation
|
Hi @wittkung! 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! |
Summary
First of all, huge thanks to @Cyan4973, @terrelln, @felixhandte, @senhuang42, and the entire Zstandard team for maintaining such an exceptional, industry-standard compression project.
This pull request introduces an AArch64 NEON SIMD vectorization path for
ZSTD_isRLE()inlib/compress/zstd_compress.c, accelerating Run-Length / All-Zero block detection from the current 4xsize_t(32-byte) scalar unrolling to 64-byte NEON vector unrolling per loop iteration on 64-bit ARM platforms (Apple Silicon, AWS Graviton / Neoverse, Ampere Altra, and Cortex-A).Historical Context & Upstream Invariants
ZSTD_isRLE:dcbbf7c("Unroll isRLE loop"), @senhuang42 optimized single-byte RLE detection into a 4xsize_t(32-byte) scalar unrolled loop viaMEM_readST(). This PR naturally extends that work by vectorizing the unrolled loop with 128-bit NEON execution units on AArch64.zstd_compress.c(lines 4161–4164, 4448–4451),ZSTD_isRLEis strictly guarded by!zc->isFirstBlockto preserve backward compatibility with legacy decoder CLIs ("should consume all input"error. This PR strictly retains all existing caller preconditions.ZSTD_maybeRLE):zstd_compress.clines 3639–3645,ZSTD_maybeRLE(nbSeqs < 4 && nbLits < 10) filters outZSTD_isRLE()is ever called, ensuring absolute zero CPU overhead on standard compression workloads.convertSequences_noRepcodes).Architectural & Vectorization Mechanics
1. 64-Byte Parallel Vector Execution
const uint8x16_t vval = vdupq_n_u8(value).vld1q_u8and compares them againstvvalvia bitwise XOR (veorq_u8).q_accumusing a 2-level binary tree:q_accum = (q0 | q1) | (q2 | q3)d0,d1) viavgetq_lane_u64. If(d0 | d1) != 0, a mismatch is detected immediately, exiting the function with2. Pointer Alignment & Memory Access Safety
vld1q_u8is backed by unaligned vector loads (ldr qN, [xN]/ld1 {vN.16b}, [xN]) which are natively handled in hardware for Normal memory with zero alignment fault risk.(uintptr_t)ip & 15) was intentionally omitted to avoid branch divergence and register pressure on short-lived checks; modern ARM Load-Store Units (LSUs) with dual 128-bit pipelines absorb unaligned cache-line crossings seamlessly.Mathematical Proof of Correctness & Boundary Safety
Let$L = \text{length}$ be the total input buffer length in bytes.
The initial prefix bytes
ZSTD_count(ip + 1, ip, ip + prefixLength). If any mismatch occurs in the prefix, the function returnsThe remaining slice length
The NEON loop consumes 64 bytes (
The subsequent scalar loop advances by
An explicit
if (length <= 1) return (int)length;check is placed at the entry point to guarantee defensive immunity against zero-length buffer anomalies.Benchmarks & Performance Results
Test Environment & Hardware Specifications
arm64-apple-darwin)arm64-apple-darwin25.6.0)clang -O3 -Wall -Wextra -DNDEBUGclock_gettime(CLOCK_MONOTONIC)), 200,000 sampling iterations per scenario on 128 KB Zstandard max block size and 64 MB streaming ring buffer.Microbenchmark: 128 KB All-Zero / RLE Block Scanning
size_tBaselineSilesia Corpus End-to-End Compression Regression Check
Evaluated on standard corpora with
zstd -bat levels 1, 3, 6, 9:ZSTD_maybeRLE).Quality & Compliance Checklist
ZSTD_ARCH_ARM_NEONinfrastructure defined inlib/common/compiler.h. No modifications to Makefiles or CMakeLists.txt.-Wdeclaration-after-statementpasses with 0 warnings).MOREFLAGS="-Wall -Wextra -Werror -Wdeclaration-after-statement -Wshadow -Wcast-qual" make libzstd.a(0 errors, 0 warnings).#if defined(ZSTD_ARCH_ARM_NEON) && (defined(__aarch64__) || defined(_M_ARM64))ensuring seamless portability across GCC, Clang, Apple Clang, and MSVC (Windows on ARM).ZSTD_isRLE()is a pure predicate. Output.zstbyte streams remain 100% bit-exact identical across all architectures.make check(all unit and CLI regression tests pass),tests/fuzzer(RLE detection tests), andtests/playTests.sh.