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).