FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
ans_stage.h
Go to the documentation of this file.
1#pragma once
2
25#include "stage/stage.h"
26#include "fzm_format.h"
27#include "backend/types.h"
28#include <cstdint>
29#include <cstring>
30#include <stdexcept>
31#include <string>
32#include <unordered_map>
33#include <vector>
34
35namespace fz {
36
37class ANSStage : public Stage {
38public:
39 // Vendored dietgpu tree (modules/coders/ans/dietgpu/) uses inline NVPTX
40 // lanemask assembly (%laneid, %lanemask_lt/le/gt/ge) and a hardcoded
41 // 32-lane warp model with no HIP/SYCL translation — excluded rather than
42 // ported (see memory/hip_sycl_backend_plan.md, Phase 1.4). Its .cu
43 // sources aren't compiled on HIP (CMakeLists.txt), so this must hide
44 // Stage::isSupportedOnBackend() rather than override it — see that
45 // method's doc comment for why it's static, not virtual.
46 static constexpr bool isSupportedOnBackend() {
47#if defined(FZGMOD_BACKEND_HIP) || defined(FZGMOD_BACKEND_SYCL)
48 return false;
49#else
50 return true;
51#endif
52 }
53
54 ANSStage() = default;
55 ~ANSStage() override = default;
56
57 // ── Configuration ─────────────────────────────────────────────────────────
58
64 void setProbBits(uint8_t pb) { prob_bits_ = pb; }
65 uint8_t getProbBits() const { return prob_bits_; }
66
67 // ── Stage control ─────────────────────────────────────────────────────────
68 void setInverse(bool inv) override { is_inverse_ = inv; }
69 bool isInverse() const override { return is_inverse_; }
70
71 // D2H copies occur in both encode (header readback) and decode (header peek).
72 bool isGraphCompatible() const override { return false; }
73
74 // dietGPU requires input aligned to 4 bytes (kANSRequiredAlignment).
75 size_t getRequiredInputAlignment() const override { return 4; }
76
77 // ── Pool lifecycle ────────────────────────────────────────────────────────
78
84 void onFinalize(size_t estimated_inlen, MemoryPool* pool) override;
85
86 size_t estimateDeviceFootprintBytes(size_t inlen) const override;
87
88 size_t estimateScratchBytes(const std::vector<size_t>& input_sizes) const override;
89
90 // ── Execution ─────────────────────────────────────────────────────────────
91 void execute(
92 fz::stream_t stream,
93 MemoryPool* pool,
94 const std::vector<void*>& inputs,
95 const std::vector<void*>& outputs,
96 const std::vector<size_t>& sizes
97 ) override;
98
99 // ── Metadata ──────────────────────────────────────────────────────────────
100 std::string getName() const override { return "ANS"; }
101 size_t getNumInputs() const override { return 1; }
102 size_t getNumOutputs() const override { return 1; }
103
104 std::vector<size_t> estimateOutputSizes(
105 const std::vector<size_t>& input_sizes
106 ) const override {
107 if (input_sizes.empty()) return {0};
108 if (!is_inverse_) {
109 // ANS worst-case expansion is ~1.25× per block plus fixed header overhead.
110 // 2× + 8 KiB is a conservative but safe bound for all realistic inputs.
111 return {input_sizes[0] * 2 + 8192};
112 }
113 // original_bytes_ is restored from the serialized FZM header before execute();
114 // fall back to input size if deserializeHeader() has not yet been called.
115 return {original_bytes_ > 0 ? original_bytes_ : input_sizes[0]};
116 }
117
118 std::unordered_map<std::string, size_t>
119 getActualOutputSizesByName() const override {
120 return {{"output", actual_output_size_}};
121 }
122
123 size_t getActualOutputSize(int index) const override {
124 return (index == 0) ? actual_output_size_ : 0;
125 }
126
127 // ── Type system ───────────────────────────────────────────────────────────
128 uint16_t getStageTypeId() const override {
129 return static_cast<uint16_t>(StageType::ANS);
130 }
131
132 // Byte-transparent: opt out of pipeline type-compatibility checking.
133 uint8_t getOutputDataType(size_t /*output_index*/) const override {
134 return static_cast<uint8_t>(DataType::UNKNOWN);
135 }
136 uint8_t getInputDataType(size_t /*input_index*/) const override {
137 return static_cast<uint8_t>(DataType::UNKNOWN);
138 }
139
140 // ── Serialization ─────────────────────────────────────────────────────────
141 size_t serializeHeader(
142 size_t /*output_index*/, uint8_t* buf, size_t max_size
143 ) const override {
144 if (max_size < 12) return 0;
145 buf[0] = prob_bits_;
146 buf[1] = buf[2] = buf[3] = 0;
147 std::memcpy(buf + 4, &original_bytes_, sizeof(uint64_t));
148 return 12;
149 }
150
151 void deserializeHeader(const uint8_t* buf, size_t size) override {
152 if (size >= 1)
153 prob_bits_ = buf[0];
154 if (size >= 12)
155 std::memcpy(&original_bytes_, buf + 4, sizeof(uint64_t));
156 }
157
158 size_t getMaxHeaderSize(size_t /*output_index*/) const override { return 12; }
159
160 void saveState() override {
161 saved_prob_bits_ = prob_bits_;
162 saved_original_bytes_ = original_bytes_;
163 saved_output_size_ = actual_output_size_;
164 }
165
166 void restoreState() override {
167 prob_bits_ = saved_prob_bits_;
168 original_bytes_ = saved_original_bytes_;
169 actual_output_size_ = saved_output_size_;
170 }
171
172private:
173 bool is_inverse_ = false;
174 uint8_t prob_bits_ = 10; // kANSDefaultProbBits
175 uint64_t original_bytes_ = 0; // set by forward execute; used by inverse estimateOutputSizes
176 size_t actual_output_size_ = 0;
177
178 // Capacity (input bytes) of current scratch allocation. Grow-only.
179 size_t cap_bytes_ = 0;
180
181 // Persistent scratch device pointers, sub-allocated from MemoryPool.
182 // All are null until initScratch() is called.
183 uint32_t* d_temp_histogram_ = nullptr; // uint32_t[256]
184 void* d_table_ = nullptr; // uint4[256] (encode table: pdf/cdf/mul/shift)
185 uint8_t* d_compressed_blocks_ = nullptr; // uint8_t[max_blocks * kUncoalescedStride]
186 uint32_t* d_compressed_words_ = nullptr; // uint32_t[max_blocks]
187 uint32_t* d_comp_words_prefix_ = nullptr; // uint32_t[max_blocks]
188 void* d_temp_prefix_sum_ = nullptr; // CUB temp storage (nullptr when blocks ≤ 512)
189 uint32_t* d_decode_table_ = nullptr; // uint32_t[1 << prob_bits_]
190
191 // D2H readback buffer for ANSCoalescedHeader after forward encode.
192 // Stored as raw bytes to avoid pulling the dietgpu headers into this header.
193 // sizeof(ANSCoalescedHeader) == 32.
194 uint8_t last_header_bytes_[32] = {};
195
196 // Histogram launch params — computed in initScratch(), reused every execute().
197 int hist_grid_dim_ = 0;
198 int hist_block_dim_ = 0;
199 int hist_shmem_use_ = 0;
200 int hist_r_per_block_ = 0;
201
202 // saveState / restoreState snapshots
203 uint8_t saved_prob_bits_ = 10;
204 uint64_t saved_original_bytes_ = 0;
205 size_t saved_output_size_ = 0;
206
207 // Allocates all 7 scratch buffers from pool and computes histogram launch
208 // params for the given input capacity. Replaces any previous allocation.
209 void initScratch(size_t inlen, MemoryPool* pool);
210
211 // execute()'s forward/inverse bodies, split into separate functions so that
212 // no single function holds both branches' local variables, kernel launches,
213 // and conditional (vGPU) code paths at once — see ans_stage.cu for why this
214 // matters (an nvc++ host-codegen bug producing a misaligned stack-argument
215 // store for CUDA kernel launches in the combined function).
216 void executeForward(fz::stream_t stream, MemoryPool* pool,
217 uint8_t* in, uint8_t* out, size_t byte_size);
218 void executeInverse(fz::stream_t stream, MemoryPool* pool,
219 uint8_t* in, uint8_t* out);
220
221 // Per-block scratch stride (bytes): ANSWarpState (128 B) + max raw compressed
222 // block (5120 B = roundUp(4096 + 4096/4, 16)). Derived from dietGPU constants.
223 static constexpr size_t kUncoalescedStride = 128 + 5120; // 5248
224};
225
226} // namespace fz
FZM binary file format definitions — structs, enums, and helpers.
Definition algorithms.h:48
@ ANS
rANS entropy coder (GPU, via dietGPU)
@ UNKNOWN
Byte-transparent stages: skip type checking at finalize()
Base class interface for all compression stages.
Backend-neutral GPU type aliases.