-
-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Open-source GPU decompression for the standard formats — batch-decode LZ4 (and, on the roadmap, Snappy, GDeflate, and Zstd) on an NVIDIA GPU. Decode-only, batch-oriented, auditable, fail-closed, and diff-tested against the reference implementations.
- Repository: https://github.com/iderex/cudec
- License: Apache-2.0
- Design record: MASTERPLAN
- Measured baselines: BENCHMARKS
This wiki is a public reference. It tracks what cudec does today, not what it aims to do; anything not listed as implemented below is not implemented yet.
GPU decompression matters wherever decode throughput is the bottleneck: asset streaming, analytics scans, ML data loading, checkpoint restore. NVIDIA's nvCOMP is the production-grade library in this space, but it has been proprietary since v2.3; there is no maintained open-source library that decodes the standard formats on the GPU. cudec fills that gap — not on price (nvCOMP is free to use) but on the properties a closed binary cannot offer: auditability (every bounds check is readable, tested, and fuzz-diffed), portability (a HIP port is a planned milestone), and hackability.
The scope is deliberate: decode-only (compression stays on the CPU, where the encoders and the flexibility live) and batch-oriented (the GPU wins when thousands of independent chunks decode in parallel — a single small file over a cold PCIe bus loses to the CPU, and cudec does not claim otherwise).
cudec is under active development. This table is the honest state of the tree and is the source of truth for what works today.
| Format / capability | Milestone | Status | ABI entry point |
|---|---|---|---|
| LZ4 block decode (batch, device-resident) | M1 | implemented | cudec_lz4_decompress_batch |
LZ4 frame decode (.lz4, block-independent subset) |
M2 | implemented | cudec_lz4f_decompress |
| LZ4 pinned-host streaming decode | M2 | implemented | cudec_lz4_decompress_stream |
| Snappy decode | M3 | planned | — |
| GDeflate decode | M4 | planned | — |
| Zstd decode | M5 | planned | — |
| HIP portability (AMD) | M6 | planned | — |
Notes on the implemented subset:
-
LZ4 frame decodes the block-independent subset. A valid frame that
uses a feature cudec does not decode yet — block-linked mode (liblz4's
frame-compressor default) or a dictionary id — returns
CUDEC_ERR_UNSUPPORTED, which is distinct from a corrupt-input error. The header, block, and content checksums and the optional declared content size are verified fail-closed when present. - All three paths are fail-closed: a malformed, truncated, or hostile bitstream produces a defined error, never an out-of-bounds access and never partial output presented as success. Every reject path has a negative test, and decode output is diff-tested against liblz4 on real and mutated corpora.
The public surface is a single C header,
include/cudec.h.
It is C-compatible, requires no CUDA headers, never throws across the boundary,
and never reports output it did not fully validate. Every function returns a
cudec_status:
| Status | Meaning |
|---|---|
CUDEC_OK |
success |
CUDEC_ERR_INVALID_ARGUMENT |
a NULL/misaligned argument, empty batch, or over-limit batch — rejected, no launch |
CUDEC_ERR_CORRUPT_INPUT |
the bitstream failed validation |
CUDEC_ERR_OUTPUT_TOO_SMALL |
the destination capacity is too small for the decoded output |
CUDEC_ERR_CUDA |
a device or host resource failure |
CUDEC_ERR_NOT_IMPLEMENTED |
reserved; defined in the ABI, not currently returned by any entry point |
CUDEC_ERR_UNSUPPORTED |
a valid frame using a feature outside cudec's supported subset |
The header is the authoritative contract for each entry point — argument
validation, the async/sync semantics of the batch call, and the fixed 16-byte
cudec_chunk_result layout the device writes per chunk. The
C API reference is a browsable, function-by-function summary
of it, including the reusable streaming context
(cudec_stream_ctx_create / cudec_lz4_decompress_stream_ctx /
cudec_stream_ctx_destroy) that repeated-decode callers should prefer over the
one-shot streaming entry point.
Two builds. The host-only build needs only a C compiler and compiles the ABI and version surface — not the decoder. The CUDA build is the decoder: opt-in, fail-closed, and maintained through a digest-pinned dev container; a GPU is required only for the GPU-labeled tests, not for the build.
Host-only build (the ABI and version surface; CMake ≥ 3.24 and a C compiler, no CUDA toolchain):
cmake -B build && cmake --build buildCUDA build (the decoder) — build everything and run the host-side test subset (no GPU needed):
docker run --rm -v "$PWD:/w" -w /w \
nvidia/cuda:12.6.2-devel-ubuntu24.04@sha256:738fba0fbdb225b7a2931c58a5c8f03a84d3cd2f6a84975826a157339ef750b8 \
sh -c "apt-get update -q && apt-get install -yq cmake >/dev/null && \
cmake -B build-cuda -DCUDEC_ENABLE_CUDA=ON && \
cmake --build build-cuda -j && \
ctest --test-dir build-cuda --no-tests=error -LE gpu --output-on-failure"To run the full suite including the on-device tests, add --gpus all to the
docker run line and drop -LE gpu. The CUDA engine is opt-in and fail-closed:
-DCUDEC_ENABLE_CUDA=ON errors without a toolchain rather than silently falling
back to host-only.
cudec decodes untrusted input by design — compressed bitstreams are classic attack surface, and a GPU decoder that trusts its input has a very wide blast radius. The posture is central, not incidental:
- fail-closed decoding: a malformed, truncated, or hostile bitstream produces a defined error, never an out-of-bounds read or write and never partial output presented as success;
- every reject path has an explicit negative test, and decode output is fuzz-diffed against the CPU reference implementations;
- all size and offset arithmetic is overflow-checked before use;
- the toolchain and all CI actions are pinned to exact hashes.
Report a suspected vulnerability (for example a crafted bitstream causing memory corruption) privately via GitHub private vulnerability reporting, not a public issue. See SECURITY.md.
A performance claim without its methodology is not a claim. Every number cudec
publishes carries its full methodology block — GPU model, driver, CUDA version,
corpus, chunk-size distribution, and run count — as emitted by the benchmark
harness (bench/bench_lz4); a bare number cannot be produced, by construction.
The Benchmarks page summarizes the measured LZ4 baselines (with
methodology inseparable from each figure); the raw record lives in
BENCHMARKS.md.
Corpora are fetched hash-pinned via bench/get-corpora.sh and are never
committed.
cudec is issue-driven and gate-driven: every change starts as a triaged GitHub
issue and lands as a gated PR off main. See
CONTRIBUTING.md.