FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
adaptive_bitpack_stage.h
Go to the documentation of this file.
1#pragma once
2
11#include "stage/stage.h"
12#include "fzm_format.h"
13#include "backend/types.h"
14#include "fused/fused_block/warp_op_params.h" // kMaxWarpElemsPerLane
15#include <cstdint>
16#include <cstring>
17#include <stdexcept>
18#include <string>
19#include <type_traits>
20#include <unordered_map>
21#include <vector>
22
23namespace fz {
24
40static_assert(sizeof(AdaptiveBitpackConfig) <= FZM_STAGE_CONFIG_SIZE,
41 "AdaptiveBitpackConfig must fit in FZM_STAGE_CONFIG_SIZE");
42
75template<typename T>
77 static_assert(std::is_same_v<T, int16_t> || std::is_same_v<T, int32_t>,
78 "AdaptiveBitpackStage: T must be int16_t or int32_t.");
79public:
80 AdaptiveBitpackStage() = default;
81 ~AdaptiveBitpackStage() override;
82
83 // ── Stage control ──────────────────────────────────────────────────────
84 void setInverse(bool inv) override { is_inverse_ = inv; }
85 bool isInverse() const override { return is_inverse_; }
86 // Forward (compress) is graph-capturable: execute() enqueues only
87 // device-side work and the data-dependent compressed-size readback is
88 // deferred to postStreamSync() (run after the launch, outside any capture
89 // window). The inverse path keeps a per-execute layout and is left out of
90 // graph capture, mirroring RZEStage.
91 bool isGraphCompatible() const override { return !is_inverse_; }
92
95 void setBlockSize(uint32_t n) {
96 if (n == 0 || n > 1024)
97 throw std::invalid_argument(
98 "AdaptiveBitpackStage::setBlockSize: n must be in [1, 1024], got "
99 + std::to_string(n));
100 block_size_ = n;
101 }
102 uint32_t getBlockSize() const { return block_size_; }
103
109 FusionSpec getFusionSpec() const override {
110 if (is_inverse_ || block_size_ == 0 || block_size_ % 32u != 0 ||
111 block_size_ / 32u > fused::warp::kMaxWarpElemsPerLane) return {};
112 return FusionSpec{FusionAccess::SegmentCodec, block_size_};
113 }
114
120 FusedOpDecl getFusedOp() const override {
121 if (!getFusionSpec().fusable()) return {};
122 FusedOpDecl d;
123 d.strategy = FusionStrategy::WarpRegister;
124 // Outlier mode -> the (swappable) adaptive coder; plain mode -> the true
125 // 1-byte-meta plain format its own inverse reads. Either composes into
126 // the warp chain with no other change.
127 d.op_name = outlier_selection_ ? fused_coder_ : "PlainRateCoder";
128 d.include_header = "fused/fused_block/warp_fusion.cuh";
129 // ThreadFixedRateCoderN<N>/ThreadPlainRateCoderN<N> mirror AdaptiveBitpackCoder
130 // and PlainRateCoder byte-for-byte for any N (launcher fills in <N> from the
131 // paired predictor's block size). Only known when fused_coder_ is left at its
132 // default "AdaptiveBitpackCoder" -- a caller who swapped in some other
133 // SegmentCodec sink via setFusedCoder() has no known TI policy to offer.
134 if (outlier_selection_) {
135 if (fused_coder_ == "AdaptiveBitpackCoder") d.ti_op_name = "ThreadFixedRateCoderN";
136 } else {
137 d.ti_op_name = "ThreadPlainRateCoderN";
138 }
139 return d;
140 }
141
148 if (!is_inverse_ || !std::is_same<T, int32_t>::value ||
149 block_size_ == 0 || block_size_ % 32u != 0 ||
150 block_size_ / 32u > fused::warp::kMaxWarpElemsPerLane) return {};
151 return FusionSpec{FusionAccess::SegmentCodec, block_size_};
152 }
154 if (!getInverseFusionSpec().fusable()) return {};
155 FusedOpDecl d;
156 d.strategy = FusionStrategy::WarpRegister;
157 // Inverse decode reads the true archive format: outlier -> adaptive 2-byte
158 // meta, plain -> PlainRateCoder 1-byte meta. (fused_coder_ is a
159 // forward-only encode choice; the archive it emits is AdaptiveBitpack-
160 // decodable, i.e. the adaptive path.)
161 d.op_name = outlier_selection_ ? "AdaptiveBitpackCoder" : "PlainRateCoder";
162 d.include_header = "fused/fused_block/warp_fusion.cuh";
163 d.elems_per_lane = block_size_ / 32u;
164 // Inverse always decodes the true AdaptiveBitpack-format archive regardless of
165 // what fused_coder_ was on the forward encode side, so the TI decode policy is
166 // always known here (no setFusedCoder ambiguity, unlike the forward decl above).
167 d.ti_op_name = outlier_selection_ ? "ThreadFixedRateCoderN" : "ThreadPlainRateCoderN";
168 return d;
169 }
170 size_t getFusedInverseElementCount() const override { return num_elements_; }
171
177 if (is_inverse_ || block_size_ == 0) return {};
179 d.kind = outlier_selection_
180 ? EncodingOracleKind::AdaptiveFixedRateBitpack
181 : EncodingOracleKind::PlainFixedRateBitpack;
182 d.op_name = outlier_selection_ ? "AdaptiveBitpackCoder" : "PlainBitpackCoder";
183 d.include_header = "fused/fused_block/warp_fusion.cuh";
184 d.input_data_type = static_cast<uint8_t>(getElementDataType());
185 d.unit_elems = block_size_;
186 d.exact = true;
187 d.additive = true;
188 return d;
189 }
190
196 void setFusedCoder(std::string name) { fused_coder_ = std::move(name); }
197 const std::string& getFusedCoder() const { return fused_coder_; }
198
203 void setFusedResult(size_t num_elements, size_t archive_bytes) {
204 num_elements_ = num_elements;
205 actual_output_size_ = archive_bytes;
206 }
207 void setFusedArchiveResult(size_t archive_bytes, size_t orig_bytes) override {
208 setFusedResult(orig_bytes / sizeof(T), archive_bytes);
209 }
210
215 void setOutlierSelection(bool enable) { outlier_selection_ = enable; }
216 bool getOutlierSelection() const { return outlier_selection_; }
217
218 // ── Execution ──────────────────────────────────────────────────────────
220 fz::stream_t stream,
221 MemoryPool* pool,
222 const std::vector<void*>& inputs,
223 const std::vector<void*>& outputs,
224 const std::vector<size_t>& sizes
225 ) override;
226
231 void postStreamSync(fz::stream_t stream) override;
232
233 // ── Metadata ───────────────────────────────────────────────────────────
234 std::string getName() const override { return "AdaptiveBitpack"; }
235 size_t getNumInputs() const override { return 1; }
236 size_t getNumOutputs() const override { return 1; }
237
238 std::vector<size_t> estimateOutputSizes(
239 const std::vector<size_t>& input_sizes
240 ) const override;
241
247 const std::vector<size_t>& input_sizes
248 ) const override;
249
250 std::unordered_map<std::string, size_t>
251 getActualOutputSizesByName() const override {
252 return {{"output", actual_output_size_}};
253 }
254 size_t getActualOutputSize(int index) const override {
255 return (index == 0) ? actual_output_size_ : 0;
256 }
257
258 uint16_t getStageTypeId() const override {
259 return static_cast<uint16_t>(StageType::ADAPTIVE_BITPACK);
260 }
261
262 // Forward: signed codes -> uint8 archive. Inverse: archive -> signed codes.
263 uint8_t getOutputDataType(size_t) const override {
264 return static_cast<uint8_t>(is_inverse_ ? getElementDataType()
265 : DataType::UINT8);
266 }
267 uint8_t getInputDataType(size_t) const override {
268 return static_cast<uint8_t>(is_inverse_ ? DataType::UINT8
269 : getElementDataType());
270 }
271
272 // ── Serialization ──────────────────────────────────────────────────────
273 size_t serializeHeader(size_t, uint8_t* buf, size_t max_size) const override {
274 if (max_size < sizeof(AdaptiveBitpackConfig)) return 0;
276 cfg.data_type = getElementDataType();
277 cfg.outlier_selection = outlier_selection_ ? uint8_t{1} : uint8_t{0};
278 cfg.block_size = block_size_;
279 cfg.num_elements = static_cast<uint64_t>(num_elements_);
280 std::memcpy(buf, &cfg, sizeof(cfg));
281 return sizeof(cfg);
282 }
283 void deserializeHeader(const uint8_t* buf, size_t size) override {
284 if (size < sizeof(AdaptiveBitpackConfig))
285 throw std::runtime_error("AdaptiveBitpackStage: header too small");
287 std::memcpy(&cfg, buf, sizeof(cfg));
288 block_size_ = cfg.block_size ? cfg.block_size : 32u;
289 num_elements_ = static_cast<size_t>(cfg.num_elements);
290 outlier_selection_ = (cfg.outlier_selection != 0);
291 }
292 size_t getMaxHeaderSize(size_t) const override {
293 return sizeof(AdaptiveBitpackConfig);
294 }
295
296 void saveState() override {
297 saved_block_size_ = block_size_;
298 saved_num_elements_ = num_elements_;
299 saved_actual_size_ = actual_output_size_;
300 saved_outlier_select_ = outlier_selection_;
301 }
302 void restoreState() override {
303 block_size_ = saved_block_size_;
304 num_elements_ = saved_num_elements_;
305 actual_output_size_ = saved_actual_size_;
306 outlier_selection_ = saved_outlier_select_;
307 }
308
309 size_t getNumElements() const { return num_elements_; }
310
311private:
312 bool is_inverse_ = false;
313 uint32_t block_size_ = 32;
314 bool outlier_selection_ = false;
315 size_t num_elements_ = 0;
316 size_t actual_output_size_ = 0;
322 std::string fused_coder_ = "AdaptiveBitpackCoder";
323
324 // Forward-path persistent scratch (kept alive across execute() so the
325 // compressed-size readback can be deferred to postStreamSync(), and so no
326 // allocation happens inside a captured graph replay). Grown lazily when a
327 // larger input is seen; freed in the destructor. Pool-managed (persistent).
328 uint32_t* d_cost_ = nullptr;
329 uint32_t* d_offset_ = nullptr;
330 size_t scratch_blocks_ = 0;
331 MemoryPool* scratch_pool_ = nullptr;
332 size_t fwd_num_blocks_ = 0;
333 size_t fwd_meta_region_ = 0;
334
335 uint32_t saved_block_size_ = 32;
336 bool saved_outlier_select_ = false;
337 size_t saved_num_elements_ = 0;
338 size_t saved_actual_size_ = 0;
339
340 static DataType getElementDataType() {
341 if (std::is_same<T, int16_t>::value) return DataType::INT16;
342 return DataType::INT32;
343 }
344};
345
346extern template class AdaptiveBitpackStage<int16_t>;
347extern template class AdaptiveBitpackStage<int32_t>;
348
349} // namespace fz
Definition adaptive_bitpack_stage.h:76
FusedOpDecl getInverseFusedOp() const override
Definition adaptive_bitpack_stage.h:153
void setFusedArchiveResult(size_t archive_bytes, size_t orig_bytes) override
Definition adaptive_bitpack_stage.h:207
void setBlockSize(uint32_t n)
Definition adaptive_bitpack_stage.h:95
void postStreamSync(fz::stream_t stream) override
void saveState() override
Definition adaptive_bitpack_stage.h:296
size_t serializeHeader(size_t, uint8_t *buf, size_t max_size) const override
Definition adaptive_bitpack_stage.h:273
uint8_t getInputDataType(size_t) const override
Definition adaptive_bitpack_stage.h:267
FusionSpec getFusionSpec() const override
Definition adaptive_bitpack_stage.h:109
void deserializeHeader(const uint8_t *buf, size_t size) override
Definition adaptive_bitpack_stage.h:283
std::string getName() const override
Definition adaptive_bitpack_stage.h:234
uint16_t getStageTypeId() const override
Definition adaptive_bitpack_stage.h:258
size_t getMaxHeaderSize(size_t) const override
Definition adaptive_bitpack_stage.h:292
size_t getFusedInverseElementCount() const override
Definition adaptive_bitpack_stage.h:170
uint8_t getOutputDataType(size_t) const override
Definition adaptive_bitpack_stage.h:263
std::unordered_map< std::string, size_t > getActualOutputSizesByName() const override
Definition adaptive_bitpack_stage.h:251
void execute(fz::stream_t stream, MemoryPool *pool, const std::vector< void * > &inputs, const std::vector< void * > &outputs, const std::vector< size_t > &sizes) override
EncodingOracleDecl getEncodingOracle() const override
Definition adaptive_bitpack_stage.h:176
size_t getActualOutputSize(int index) const override
Definition adaptive_bitpack_stage.h:254
size_t estimateScratchBytes(const std::vector< size_t > &input_sizes) const override
bool isGraphCompatible() const override
Definition adaptive_bitpack_stage.h:91
void setInverse(bool inv) override
Definition adaptive_bitpack_stage.h:84
void setFusedCoder(std::string name)
Definition adaptive_bitpack_stage.h:196
std::vector< size_t > estimateOutputSizes(const std::vector< size_t > &input_sizes) const override
FusionSpec getInverseFusionSpec() const override
Definition adaptive_bitpack_stage.h:147
void setOutlierSelection(bool enable)
Definition adaptive_bitpack_stage.h:215
FusedOpDecl getFusedOp() const override
Definition adaptive_bitpack_stage.h:120
void setFusedResult(size_t num_elements, size_t archive_bytes)
Definition adaptive_bitpack_stage.h:203
Definition mempool.h:82
Definition stage.h:31
FZM binary file format definitions — structs, enums, and helpers.
Definition dag.h:24
constexpr size_t FZM_STAGE_CONFIG_SIZE
Per-stage serialized config slot (bytes)
Definition fzm_format.h:65
@ ADAPTIVE_BITPACK
Per-block adaptive fixed-rate bit-plane coder (cuSZp plain mode)
DataType
Element data type identifiers used in buffer and stage descriptors.
Definition fzm_format.h:142
Base class interface for all compression stages.
Definition adaptive_bitpack_stage.h:29
uint8_t _pad[2]
Must be zero.
Definition adaptive_bitpack_stage.h:32
uint8_t outlier_selection
1 = cuSZp2 per-block plain/outlier selection.
Definition adaptive_bitpack_stage.h:31
uint32_t block_size
Elements per logical block (reset period).
Definition adaptive_bitpack_stage.h:33
DataType data_type
Signed element type (1B): INT16 / INT32.
Definition adaptive_bitpack_stage.h:30
uint64_t num_elements
Original element count (sizes the inverse output).
Definition adaptive_bitpack_stage.h:34
Host-side declaration of a local, exact encoded-size oracle.
Definition fusion.h:104
uint8_t input_data_type
DataType value; 0xFF = unknown.
Definition fusion.h:109
A stage's contribution to a generated fused kernel — the device-op it maps to, where its source lives...
Definition fusion.h:161
uint32_t elems_per_lane
Definition fusion.h:172
std::string op_name
device-op type name, e.g. "DiffNegabinary"
Definition fusion.h:163
std::string include_header
header used by the generated source
Definition fusion.h:164
std::string ti_op_name
Definition fusion.h:191
A stage's fusion contract. Stages that can participate in a fused kernel override Stage::getFusionSpe...
Definition fusion.h:51
Backend-neutral GPU type aliases.
POD parameter blocks for the warp-register predictor policies.