FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
quant_adaptive_lorenzo_stage.h
Go to the documentation of this file.
1#pragma once
2
11#include "fused/lorenzo_quant/lorenzo_quant.h" // ErrorBoundMode, resolveApproxRelMode
12#include "stage/stage.h"
13#include "fzm_format.h"
14#include "backend/types.h"
16#include <cstdint>
17#include <cstring>
18#include <stdexcept>
19#include <string>
20#include <type_traits>
21#include <unordered_map>
22#include <vector>
23
24namespace fz {
25
36 uint8_t blocks_per_tile;
37 uint8_t enable_order2;
38 uint8_t enable_centering;
39 uint8_t eb_mode;
40 uint8_t reserved[3];
41 uint32_t num_elements;
42 double abs_error_bound_f64;
45
47 : coder_block_size(32), blocks_per_tile(8), enable_order2(1),
48 enable_centering(1), eb_mode(0), reserved{0, 0, 0}, num_elements(0),
49 abs_error_bound_f64(0.0), value_base_f64(0.0), user_error_bound(0.0f) {}
50};
51static_assert(sizeof(FusedQuantAdaptiveLorenzoConfig) <= FZM_STAGE_CONFIG_SIZE,
52 "FusedQuantAdaptiveLorenzoConfig must fit in FZM_STAGE_CONFIG_SIZE");
53
88template<typename T = int32_t>
90 static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
91 "FusedQuantAdaptiveLorenzoStage requires a signed integer residual type");
92public:
93 struct Config {
94 uint32_t coder_block_size = 32;
95 uint32_t blocks_per_tile = 8;
96 bool enable_order2 = true;
97 bool enable_centering = true;
99 double error_bound = 1e-3;
106 float precomputed_value_base = 0.0f;
107 Config() = default;
108 };
109
110 FusedQuantAdaptiveLorenzoStage() { validate(); }
111 explicit FusedQuantAdaptiveLorenzoStage(const Config& config) : config_(config) { validate(); }
112
113 void setInverse(bool inv) override { is_inverse_ = inv; }
114 bool isInverse() const override { return is_inverse_; }
115
116 uint32_t getTileSize() const {
117 return config_.coder_block_size * config_.blocks_per_tile;
118 }
119
120 void setErrorBound(double eb) { config_.error_bound = eb; }
121 void setErrorBoundMode(ErrorBoundMode mode) {
122 config_.eb_mode = resolveApproxRelMode(mode, "FusedQuantAdaptiveLorenzoStage");
123 }
124 void setValueBase(float value_base) { config_.precomputed_value_base = value_base; }
125 double getErrorBound() const { return config_.error_bound; }
126 ErrorBoundMode getErrorBoundMode() const { return config_.eb_mode; }
129 double getComputedAbsErrorBound() const { return computed_abs_eb_; }
130
134 if (!decl.valid() || !decl.additive ||
135 (decl.kind != EncodingOracleKind::PlainFixedRateBitpack &&
136 decl.kind != EncodingOracleKind::AdaptiveFixedRateBitpack) ||
137 decl.input_data_type != static_cast<uint8_t>(getElementDataType()) ||
138 decl.unit_elems != config_.coder_block_size) {
139 return false;
140 }
141 bound_oracle_ = decl;
142 has_bound_oracle_ = true;
143 return true;
144 }
145
146 bool hasBoundEncodingOracle() const { return has_bound_oracle_; }
147 EncodingOracleKind getBoundEncodingOracleKind() const {
148 return has_bound_oracle_ ? bound_oracle_.kind
149 : EncodingOracleKind::PlainFixedRateBitpack;
150 }
151
152 FusionSpec getFusionSpec() const override {
153 if (is_inverse_ || !has_bound_oracle_) return {};
154 return FusionSpec{FusionAccess::TileSelector, getTileSize(),
155 config_.coder_block_size};
156 }
157
158 std::vector<FusedAuxOutputDecl> getFusedAuxOutputs() const override {
159 if (!getFusionSpec().fusable()) return {};
160 return {
162 static_cast<uint8_t>(DataType::UINT8), getTileSize(),
163 2u, 0u},
165 static_cast<uint8_t>(getElementDataType()), getTileSize(),
166 0u, 1u},
167 };
168 }
169
171 fz::stream_t stream,
172 MemoryPool* pool,
173 const std::vector<void*>& inputs,
174 const std::vector<void*>& outputs,
175 const std::vector<size_t>& sizes
176 ) override;
177
178 std::string getName() const override { return "FusedQuantAdaptiveLorenzo"; }
179 size_t getNumInputs() const override { return is_inverse_ ? 3 : 1; }
180 size_t getNumOutputs() const override { return is_inverse_ ? 1 : 3; }
181
182 std::vector<std::string> getOutputNames() const override {
183 return {"output", "modes", "means"};
184 }
185
186 ~FusedQuantAdaptiveLorenzoStage() override { releaseScratch(); }
187
188 void postStreamSync(fz::stream_t stream) override;
189
193 bool isGraphCompatible() const override { return false; }
194
196 const std::vector<size_t>& input_sizes
197 ) const override;
198
199 std::vector<size_t> estimateOutputSizes(
200 const std::vector<size_t>& input_sizes
201 ) const override {
202 if (input_sizes.empty()) return is_inverse_ ? std::vector<size_t>{0}
203 : std::vector<size_t>{0, 0, 0};
204 if (is_inverse_) return {input_sizes[0] / sizeof(T) * sizeof(float)};
205 const size_t n = input_sizes[0] / sizeof(float);
206 const size_t tiles = numTiles(n);
207 return {n * sizeof(T), (tiles + 3) / 4, tiles * sizeof(T)};
208 }
209
210 void saveState() override { saved_output_sizes_ = actual_output_sizes_; }
211 void restoreState() override {
212 if (!saved_output_sizes_.empty()) actual_output_sizes_ = saved_output_sizes_;
213 }
214
215 std::unordered_map<std::string, size_t>
216 getActualOutputSizesByName() const override {
217 auto names = getOutputNames();
218 std::unordered_map<std::string, size_t> r;
219 for (size_t i = 0; i < names.size() && i < actual_output_sizes_.size(); ++i)
220 r[names[i]] = actual_output_sizes_[i];
221 return r;
222 }
223
224 size_t getActualOutputSize(int index) const override {
225 return (index >= 0 && index < static_cast<int>(actual_output_sizes_.size()))
226 ? actual_output_sizes_[index] : 0;
227 }
228
229 void setFusedSideOutput(int output_index, size_t bytes) override {
230 if (actual_output_sizes_.size() < 3) actual_output_sizes_.resize(3, 0);
231 if (output_index == 1 || output_index == 2)
232 actual_output_sizes_[static_cast<size_t>(output_index)] = bytes;
233 }
234
235 uint16_t getStageTypeId() const override {
236 return static_cast<uint16_t>(StageType::FUSED_QUANT_ADAPTIVE_LORENZO);
237 }
238
239 uint8_t getOutputDataType(size_t output_index) const override {
240 if (is_inverse_) return static_cast<uint8_t>(DataType::FLOAT32);
241 return static_cast<uint8_t>(output_index == 1 ? DataType::UINT8
242 : getElementDataType());
243 }
244 uint8_t getInputDataType(size_t input_index) const override {
245 if (!is_inverse_) return static_cast<uint8_t>(DataType::FLOAT32);
246 return static_cast<uint8_t>(input_index == 1 ? DataType::UINT8
247 : getElementDataType());
248 }
249
250 size_t serializeHeader(size_t /*output_index*/, uint8_t* buf, size_t max_size) const override {
251 if (max_size < sizeof(FusedQuantAdaptiveLorenzoConfig))
252 throw std::runtime_error("FusedQuantAdaptiveLorenzoStage: header buffer too small");
254 cfg.coder_block_size = static_cast<uint8_t>(config_.coder_block_size);
255 cfg.blocks_per_tile = static_cast<uint8_t>(config_.blocks_per_tile);
256 cfg.enable_order2 = config_.enable_order2 ? 1u : 0u;
257 cfg.enable_centering = config_.enable_centering ? 1u : 0u;
258 cfg.eb_mode = static_cast<uint8_t>(config_.eb_mode);
259 cfg.num_elements = static_cast<uint32_t>(num_elements_);
260 cfg.abs_error_bound_f64 = computed_abs_eb_;
261 cfg.value_base_f64 = computed_value_base_;
262 cfg.user_error_bound = static_cast<float>(config_.error_bound);
263 std::memcpy(buf, &cfg, sizeof(cfg));
264 return sizeof(cfg);
265 }
266
267 void deserializeHeader(const uint8_t* buf, size_t size) override {
268 if (size < sizeof(FusedQuantAdaptiveLorenzoConfig))
269 throw std::runtime_error("FusedQuantAdaptiveLorenzoStage: header too small");
271 std::memcpy(&cfg, buf, sizeof(cfg));
272 config_.coder_block_size = cfg.coder_block_size;
273 config_.blocks_per_tile = cfg.blocks_per_tile;
274 config_.enable_order2 = (cfg.enable_order2 != 0);
275 config_.enable_centering = (cfg.enable_centering != 0);
276 config_.eb_mode = static_cast<ErrorBoundMode>(cfg.eb_mode);
277 num_elements_ = cfg.num_elements;
278 computed_abs_eb_ = cfg.abs_error_bound_f64;
279 computed_value_base_ = cfg.value_base_f64;
280 validate();
281 }
282
283 size_t getMaxHeaderSize(size_t /*output_index*/) const override {
284 return sizeof(FusedQuantAdaptiveLorenzoConfig);
285 }
286
287private:
288 Config config_;
289 EncodingOracleDecl bound_oracle_;
290 bool has_bound_oracle_ = false;
291 bool is_inverse_ = false;
292 size_t num_elements_ = 0;
295 double computed_abs_eb_ = 0.0;
296 double computed_value_base_ = 0.0;
297 std::vector<size_t> actual_output_sizes_{0, 0, 0};
298 std::vector<size_t> saved_output_sizes_;
299
300 // Forward scratch — identical role to AdaptiveLorenzoStage's, reused for
301 // the inverse's offset recomputation too (same pattern as that class).
302 uint8_t* d_modes_dense_ = nullptr;
303 T* d_means_dense_ = nullptr;
304 uint32_t* d_flags_ = nullptr;
305 uint32_t* d_offsets_ = nullptr;
306 size_t scratch_tiles_ = 0;
307 MemoryPool* scratch_pool_ = nullptr;
308 size_t pending_tiles_ = 0;
309
310 // Inverse-only scratch: the reconstructed integer codes before the final
311 // dequant pass (this stage's inverse always ends in float, unlike
312 // AdaptiveLorenzoStage's, which stops at T).
313 T* d_inverse_codes_ = nullptr;
314 size_t inverse_codes_elems_ = 0;
315 MemoryPool* inverse_scratch_pool_ = nullptr;
316
317 size_t ensureScratch(size_t num_tiles, MemoryPool* pool, fz::stream_t stream);
318 size_t ensureInverseScratch(size_t n, MemoryPool* pool, fz::stream_t stream);
319 void releaseScratch();
320
321 size_t numTiles(size_t n) const {
322 const size_t t = getTileSize();
323 return (n + t - 1) / t;
324 }
325
326 void validate() const {
327 if (config_.coder_block_size != 32)
328 throw std::invalid_argument(
329 "FusedQuantAdaptiveLorenzoStage: coder_block_size must be 32");
330 if (config_.blocks_per_tile < 1 || config_.blocks_per_tile > 32)
331 throw std::invalid_argument(
332 "FusedQuantAdaptiveLorenzoStage: blocks_per_tile must be in [1, 32], got "
333 + std::to_string(config_.blocks_per_tile));
334 }
335
336 static DataType getElementDataType() { return fused::dataTypeOf<T>(); }
337};
338
339extern template class FusedQuantAdaptiveLorenzoStage<int32_t>;
340
341} // namespace fz
Per-tile adaptive multi-order Lorenzo predictor with centering. Lossless.
Definition quant_adaptive_lorenzo_stage.h:89
void postStreamSync(fz::stream_t stream) override
size_t getActualOutputSize(int index) const override
Definition quant_adaptive_lorenzo_stage.h:224
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
void setInverse(bool inv) override
Definition quant_adaptive_lorenzo_stage.h:113
FusionSpec getFusionSpec() const override
Definition quant_adaptive_lorenzo_stage.h:152
double getComputedAbsErrorBound() const
Definition quant_adaptive_lorenzo_stage.h:129
bool bindDownstreamEncodingOracle(const EncodingOracleDecl &decl) override
Definition quant_adaptive_lorenzo_stage.h:133
size_t estimateScratchBytes(const std::vector< size_t > &input_sizes) const override
size_t serializeHeader(size_t, uint8_t *buf, size_t max_size) const override
Definition quant_adaptive_lorenzo_stage.h:250
uint8_t getInputDataType(size_t input_index) const override
Definition quant_adaptive_lorenzo_stage.h:244
bool isGraphCompatible() const override
Definition quant_adaptive_lorenzo_stage.h:193
void deserializeHeader(const uint8_t *buf, size_t size) override
Definition quant_adaptive_lorenzo_stage.h:267
std::string getName() const override
Definition quant_adaptive_lorenzo_stage.h:178
std::unordered_map< std::string, size_t > getActualOutputSizesByName() const override
Definition quant_adaptive_lorenzo_stage.h:216
std::vector< std::string > getOutputNames() const override
Definition quant_adaptive_lorenzo_stage.h:182
std::vector< size_t > estimateOutputSizes(const std::vector< size_t > &input_sizes) const override
Definition quant_adaptive_lorenzo_stage.h:199
uint8_t getOutputDataType(size_t output_index) const override
Definition quant_adaptive_lorenzo_stage.h:239
std::vector< FusedAuxOutputDecl > getFusedAuxOutputs() const override
Definition quant_adaptive_lorenzo_stage.h:158
void saveState() override
Definition quant_adaptive_lorenzo_stage.h:210
uint16_t getStageTypeId() const override
Definition quant_adaptive_lorenzo_stage.h:235
void setFusedSideOutput(int output_index, size_t bytes) override
Definition quant_adaptive_lorenzo_stage.h:229
size_t getMaxHeaderSize(size_t) const override
Definition quant_adaptive_lorenzo_stage.h:283
Definition mempool.h:82
Definition stage.h:31
Compile-time C++ type -> DataType enum mapping, shared by the fused stages that dispatch on multiple ...
FZM binary file format definitions — structs, enums, and helpers.
Fused Lorenzo predictor and quantizer stage.
Definition dag.h:24
ErrorBoundMode
Definition lorenzo_quant.h:42
@ ABS
Absolute error bound.
ErrorBoundMode resolveApproxRelMode(ErrorBoundMode mode, const char *stage_name)
Definition lorenzo_quant.h:63
constexpr size_t FZM_STAGE_CONFIG_SIZE
Per-stage serialized config slot (bytes)
Definition fzm_format.h:65
@ FUSED_QUANT_ADAPTIVE_LORENZO
AdaptiveLorenzo with the upstream linear Quantizer fused into its forward kernel (FSZ "M1" partial fu...
DataType
Element data type identifiers used in buffer and stage descriptors.
Definition fzm_format.h:142
@ CompactedElements
runtime count * sizeof(element)
@ FixedBitsPerUnit
ceil(num_units * bits_per_unit / 8)
EncodingOracleKind
Registered exact encoded-size policies used by an upstream adaptive stage for an algorithmic mode dec...
Definition fusion.h:90
Base class interface for all compression stages.
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
Definition fusion.h:131
Definition quant_adaptive_lorenzo_stage.h:34
uint8_t coder_block_size
Fixed at 32.
Definition quant_adaptive_lorenzo_stage.h:35
uint8_t eb_mode
ErrorBoundMode cast to uint8_t.
Definition quant_adaptive_lorenzo_stage.h:39
double value_base_f64
NOA/PREL scan result; 0 for ABS.
Definition quant_adaptive_lorenzo_stage.h:43
float user_error_bound
Original config_.error_bound, narrow copy (debug only).
Definition quant_adaptive_lorenzo_stage.h:44
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.