Skip to content

Pipeline steps and values

Every value of every step: what it does, from the comments in the C sources (linked), and what it needs, from VALUES in src/_pipeline.py. Value 0 (auto) is resolved when an object is made; describe() shows which values the running machine can use (see the pipelines example), and the kernel page times them all.

decode

value what it does needs source
lz4-band the zero-aware decoder (lz4zero.h) for blocks compressed more than BSLZ4_LZ4ZERO_RATIO or less than BSLZ4_LZ4ZERO_DENSE, stock lz4 in between. Zero-aware wins on sparse blocks (skipped zero runs) and on dense ones (literal heavy; it never writes the empty high byte-planes), but blocks of ~8-24x are many short sequences (~90-130 per 8 kB) where stock's loop is faster. Measured by tools/bench_kernels.py, see the Every pipeline page of the documentation. lz4: zero-aware decoder below 8x and above 24x, stock lz4 in between decode.h:12
lz4-zero the zero-aware decoder for every block: POWER9, where stock lz4 is slower in every band, and Cascade Lake. lz4: zero-aware decoder for every block decode.h:22
lz4-stock LZ4_decompress_safe. lz4: LZ4_decompress_safe decode.h:25
zstd ZSTD_decompress. zstd decode.h:26
pixels no decode: the input is uncompressed pixel frames (from_frames), so the block loop reads each frame's blocks where they lie and runs only the mask, collect and dot steps. no decode: uncompressed pixel frames (from_frames); not for chunks decode.h:27

mask

value what it does needs source
pixel tested per pixel by the collect (the default: free on data whose masked pixels hold 0, as our Eigers write). tested per pixel when collecting mask.h:7
planes packed once per block per batch into the bit-plane layout (bit e % 8 of byte e / 8) and ANDed into every bit-plane straight after decode, so masked pixels are 0 before any untranspose and the block needs no mask tests. Blocks with nothing masked skip it. Wins only where masked pixels hold the dtype maximum: then they no longer stop the u16 byte skip; pure overhead on zero-filled data. ANDed into the bit-planes after decode (wins only if masked pixels hold the maximum) mask.h:9
none every pixel valid: no tests at all. every pixel valid: no mask tests mask.h:17

untranspose

value what it does needs source
lowplanes-vbmi needs AVX-512 F/BW/VL/VBMI/VBMI2, GFNI and popcnt. Bit transpose by gf2p8affineqb, 64 pixels at a time, stored straight into u16 for the dense route; the fused collect transposes only the 64-pixel groups holding data in well-compressed blocks. u16 pixels; AVX-512 VBMI, VBMI2 and GFNI untranspose.h:27
lowplanes-avx2 needs AVX2 and popcnt. Fused collect: planes ORed into a bitmap of non-empty 64-pixel groups, only those transposed (kcb's AVX2 kernel) and compacted; ne % 256 == 0, any block size (worked in tiles of BSLZ4_LOWPLANES_TILE pixels). Dense route: kcb's u8 transpose, widened to u16. u16 pixels; AVX2 untranspose.h:32
lowplanes-vsx needs POWER8+ (vgbbd). Fused collect: per 64-pixel group two vec_perm rounds and vec_gb give the pixel bytes; all-zero groups and words are skipped. The dense route takes kcb. u16 pixels; POWER8 VSX untranspose.h:38
lowplanes-c portable C, always available: kcb's u8 transpose of the low planes, then a SWAR collect of the pixels > cut. The dense route takes kcb. u16 pixels; portable C untranspose.h:42
kcb kcb, any dtype and block: dispatches SSE2, AVX2 or AVX-512 itself (no NEON code); the full bit and byte transpose into the pixel block, collect separate. kcb's bit transpose (SSE2/AVX2/AVX-512 dispatched inside) untranspose.h:45
neon bitshuffle's NEON kernels (bshuf_trans_byte_bitrow_NEON, then bshuf_shuffle_bit_eightelem_NEON); AArch64 only, any dtype, into the pixel block. bitshuffle's NEON kernels; aarch64 untranspose.h:48
scalar bitshuffle's scalar kernels (bshuf_trans_byte_bitrow_scal, then bshuf_shuffle_bit_eightelem_scal); any CPU, any dtype, into the pixel block. bitshuffle's scalar kernels untranspose.h:51

collect

value what it does needs source
avx512cs AVX-512 F+BW+VL+VBMI2: compress-store of the selected pixels and their addresses; u16/u32. u16/u32 pixels; AVX-512 BW/VL and VBMI2 collect.hpp:19
avx2cs AVX2: u16 compacts the selected lanes with a table, u32 walks the set bits; u16/u32. u16/u32 pixels; AVX2 collect.hpp:21
vsx POWER8+ (needs -maltivec -mvsx); u16/u32. u16/u32 pixels; POWER VSX collect.hpp:23
neon AArch64 ASIMD (ARMv8-A baseline); u16/u32. u16/u32 pixels; aarch64 NEON collect.hpp:24
sse2 x86-64 baseline, always present; u16/u32. u16/u32 pixels; SSE2 collect.hpp:25
scalar the plain scalar loop, any dtype; every dtype other than u16/u32 takes it whatever the value. any pixel type collect.hpp:26

route

value what it does needs source
ratio per block: the sparse route when the block is compressed more than BSLZ4_ROUTE_RATIO (8) times (blocksize > 8 * compressed bytes), the dense route otherwise. Well-compressed blocks are mostly zero, so a list of the few non-zero pixels is cheaper than a pass over the block. per block: sparse when compressed more than 8x driver.c:18
dense every block untransposed to the pixel block and the dot taken over every pixel of it. dot over every pixel of every block driver.c:27
sparse every block reduced to the list of its non-zero pixels (mask applied), the dot taken over the list; for u16 blocks < 256 the untranspose is fused with the list's collect. dot over the non-zero pixels of every block driver.c:29
bpp as the ratio rule, in bits per pixel: the sparse route when the block is compressed below BSLZ4_ROUTE_BPP (2) bits per pixel, whatever the pixel width (the ratio rule's 8x is 2 bits per pixel for u16 only). per block: sparse when compressed below 2 bits per pixel driver.c:24

dot

value what it does needs source
csc general CSC: any matrix, one (bin, weight) entry per pixel/bin pair. any matrix registry.c:50
padded every pixel one ascending run of consecutive bins, at most 64 wide: per row a first bin and a fixed width of weights, zero padded; no index read per entry. Rows are per pixel, or only the pixels with entries when under half have any (listed). each pixel's bins one consecutive run (at most 64) registry.c:52
padded-avx2 the padded layout with AVX2+FMA row kernels on the dense route, for widths <= 8 (wider: scalar). as padded; AVX2 + FMA on the dense route registry.c:57
csc-run CSC where each pixel's bins are one ascending run of consecutive bins: stores only the first bin per pixel beside the weights; any width. each pixel's bins one consecutive run registry.c:59
csc-nosplit a histogram: at most one bin per pixel, weight exactly 1; stores that bin per pixel, no weights. at most one bin per pixel, weight 1 (a histogram) registry.c:62
bsb-csr any matrix, for ones where most pixels have no entry (rings): per decode block a CSR of the bins it touches, so the dense route walks only those bins with register accumulators; the sparse route uses the nested CSC. any matrix; for few occupied pixels (rings) registry.c:64
bsb-csr-nosplit bsb-csr for a histogram (one bin per pixel, weight 1). as csc-nosplit registry.c:69
csc-permute a permutation: each pixel its own bin, weight 1; the pixel value is stored (not added) at its bin, so the output is the re-ordered image, in the pixel dtype rather than a float64 powder. a permutation: one bin per pixel, weight 1, each bin used once; output in the pixel dtype registry.c:71
csc-int general CSC with u32 fixed-point weights round(w * 2b) (unsigned, so no negative weights), integer pixel dtypes only; exact int64 sums of w * v, rescaled by 2-b afterwards. integer pixels; fixed-point u32 weights, exact int64 sums registry.c:75
csc-run-int csc-run's layout (first bin per pixel) with u32 fixed-point weights and an int64 powder, as csc-int. integer pixels; as csc-run with fixed-point u32 weights registry.c:79
csc-run-int16 as csc-run-int with u16 weights. integer pixels; as csc-run with fixed-point u16 weights registry.c:81
csc-nosplit-int csc-nosplit's layout (one bin per pixel, weight 1) summed in int64: no weights, no multiply, no float conversion; the dense route walks tiles of a block in strided passes. Pixels of at most 32 bits, so the sums cannot overflow. The sums are int64 in the powder buffer; the driver converts each frame in place to float64 once its last block is in (a sum above 2**53 is rounded there), so the result is a float64 powder. integer pixels of at most 32 bits; csc-nosplit summed in int64, returned as float64 (one cast at the end) registry.c:82

Generated by tools/generate_docs.py from bslz4_to_sparse 0.0.22a2 (git v0.0.22-13-g610ed27, sources sha256 6929bd75ab7a).