FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
adaptive_lorenzo_stage.h
Go to the documentation of this file.
1#pragma once
2
8#include "stage/stage.h"
9#include "fzm_format.h"
10#include "backend/types.h"
11#include <algorithm>
12#include <array>
13#include <cstdint>
14#include <cstring>
16#include "fused/lorenzo_quant/lorenzo_quant.h" // ErrorBoundMode, resolveApproxRelMode — FusedQuantAdaptiveLorenzoStage
17
18#include <stdexcept>
19#include <string>
20#include <type_traits>
21#include <unordered_map>
22#include <vector>
23
24namespace fz {
25
41static_assert(sizeof(AdaptiveLorenzoConfig) <= FZM_STAGE_CONFIG_SIZE,
42 "AdaptiveLorenzoConfig must fit in FZM_STAGE_CONFIG_SIZE");
43
97template<typename T>
99 static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
100 "AdaptiveLorenzoStage requires a signed integer type");
101public:
102 struct Config {
107 uint32_t coder_block_size = 32;
112 uint32_t blocks_per_tile = 8;
113 bool enable_order2 = true;
114 bool enable_centering = true;
115 Config() = default;
116 };
117
118 AdaptiveLorenzoStage() { validate(); }
119 explicit AdaptiveLorenzoStage(const Config& config) : config_(config) { validate(); }
120
121 void setInverse(bool inv) override { is_inverse_ = inv; }
122 bool isInverse() const override { return is_inverse_; }
123
124 uint32_t getTileSize() const {
125 return config_.coder_block_size * config_.blocks_per_tile;
126 }
127
132 if (!decl.valid() || !decl.additive ||
133 (decl.kind != EncodingOracleKind::PlainFixedRateBitpack &&
134 decl.kind != EncodingOracleKind::AdaptiveFixedRateBitpack) ||
135 decl.input_data_type != static_cast<uint8_t>(getElementDataType()) ||
136 decl.unit_elems != config_.coder_block_size) {
137 return false;
138 }
139 bound_oracle_ = decl;
140 has_bound_oracle_ = true;
141 return true;
142 }
143
144 bool hasBoundEncodingOracle() const { return has_bound_oracle_; }
145 EncodingOracleKind getBoundEncodingOracleKind() const {
146 return has_bound_oracle_ ? bound_oracle_.kind
147 : EncodingOracleKind::PlainFixedRateBitpack;
148 }
149
150 FusionSpec getFusionSpec() const override {
151 if (is_inverse_ || !has_bound_oracle_) return {};
152 return FusionSpec{FusionAccess::TileSelector, getTileSize(),
153 config_.coder_block_size};
154 }
155
156 std::vector<FusedAuxOutputDecl> getFusedAuxOutputs() const override {
157 if (!getFusionSpec().fusable()) return {};
158 return {
160 static_cast<uint8_t>(DataType::UINT8), getTileSize(),
161 2u, 0u},
163 static_cast<uint8_t>(getElementDataType()), getTileSize(),
164 0u, 1u},
165 };
166 }
167
169 fz::stream_t stream,
170 MemoryPool* pool,
171 const std::vector<void*>& inputs,
172 const std::vector<void*>& outputs,
173 const std::vector<size_t>& sizes
174 ) override;
175
176 std::string getName() const override { return "AdaptiveLorenzo"; }
177 size_t getNumInputs() const override { return is_inverse_ ? 3 : 1; }
178 size_t getNumOutputs() const override { return is_inverse_ ? 1 : 3; }
179
180 std::vector<std::string> getOutputNames() const override {
181 return {"output", "modes", "means"};
182 }
183
184 ~AdaptiveLorenzoStage() override { releaseScratch(); }
185
189 void postStreamSync(fz::stream_t stream) override;
190
193 bool isGraphCompatible() const override { return false; }
194
197 const std::vector<size_t>& input_sizes
198 ) const override;
199
200 std::vector<size_t> estimateOutputSizes(
201 const std::vector<size_t>& input_sizes
202 ) const override {
203 if (input_sizes.empty()) return {0, 0, 0};
204 if (is_inverse_) return {input_sizes[0]};
205 const size_t n = input_sizes[0] / sizeof(T);
206 const size_t tiles = numTiles(n);
207 // modes: 2 bits per tile. means: worst case one per tile — the real
208 // length is trimmed in postStreamSync() once the scan total is known.
209 return {input_sizes[0], (tiles + 3) / 4, tiles * sizeof(T)};
210 }
211
212 void saveState() override { saved_output_sizes_ = actual_output_sizes_; }
213 void restoreState() override {
214 if (!saved_output_sizes_.empty()) actual_output_sizes_ = saved_output_sizes_;
215 }
216
217 std::unordered_map<std::string, size_t>
218 getActualOutputSizesByName() const override {
219 auto names = getOutputNames();
220 std::unordered_map<std::string, size_t> r;
221 for (size_t i = 0; i < names.size() && i < actual_output_sizes_.size(); ++i)
222 r[names[i]] = actual_output_sizes_[i];
223 return r;
224 }
225
226 size_t getActualOutputSize(int index) const override {
227 return (index >= 0 && index < static_cast<int>(actual_output_sizes_.size()))
228 ? actual_output_sizes_[index] : 0;
229 }
230
231 void setFusedSideOutput(int output_index, size_t bytes) override {
232 if (actual_output_sizes_.size() < 3) actual_output_sizes_.resize(3, 0);
233 if (output_index == 1 || output_index == 2)
234 actual_output_sizes_[static_cast<size_t>(output_index)] = bytes;
235 }
236
237 uint16_t getStageTypeId() const override {
238 return static_cast<uint16_t>(StageType::ADAPTIVE_LORENZO);
239 }
240
241 uint8_t getOutputDataType(size_t output_index) const override {
242 // The mode map is a byte stream; the residuals and means are T.
243 return static_cast<uint8_t>(output_index == 1 ? DataType::UINT8
244 : getElementDataType());
245 }
246 uint8_t getInputDataType(size_t input_index) const override {
247 return static_cast<uint8_t>(input_index == 1 ? DataType::UINT8
248 : getElementDataType());
249 }
250
251 size_t serializeHeader(size_t /*output_index*/, uint8_t* buf, size_t max_size) const override {
252 if (max_size < sizeof(AdaptiveLorenzoConfig))
253 throw std::runtime_error("AdaptiveLorenzoStage: header buffer too small");
255 cfg.data_type = getElementDataType();
256 cfg.coder_block_size = static_cast<uint8_t>(config_.coder_block_size);
257 cfg.blocks_per_tile = static_cast<uint8_t>(config_.blocks_per_tile);
258 cfg.enable_order2 = config_.enable_order2 ? 1u : 0u;
259 cfg.enable_centering = config_.enable_centering ? 1u : 0u;
260 cfg.num_elements = static_cast<uint32_t>(num_elements_);
261 std::memcpy(buf, &cfg, sizeof(cfg));
262 return sizeof(cfg);
263 }
264
265 void deserializeHeader(const uint8_t* buf, size_t size) override {
266 if (size < sizeof(AdaptiveLorenzoConfig))
267 throw std::runtime_error("AdaptiveLorenzoStage: header too small");
269 std::memcpy(&cfg, buf, sizeof(cfg));
270 config_.coder_block_size = cfg.coder_block_size;
271 config_.blocks_per_tile = cfg.blocks_per_tile;
272 config_.enable_order2 = (cfg.enable_order2 != 0);
273 config_.enable_centering = (cfg.enable_centering != 0);
274 num_elements_ = cfg.num_elements;
275 validate();
276 }
277
278 size_t getMaxHeaderSize(size_t /*output_index*/) const override {
279 return sizeof(AdaptiveLorenzoConfig);
280 }
281
282private:
283 Config config_;
284 EncodingOracleDecl bound_oracle_;
285 bool has_bound_oracle_ = false;
286 bool is_inverse_ = false;
287 size_t num_elements_ = 0;
288 std::vector<size_t> actual_output_sizes_{0, 0, 0};
289 std::vector<size_t> saved_output_sizes_;
290
291 // Persistent compaction scratch, grown when a larger input is seen. Held
292 // across calls so postStreamSync() can read the scan total after the fact.
293 uint8_t* d_modes_dense_ = nullptr;
294 T* d_means_dense_ = nullptr;
295 uint32_t* d_flags_ = nullptr;
296 uint32_t* d_offsets_ = nullptr;
297 size_t scratch_tiles_ = 0;
298 MemoryPool* scratch_pool_ = nullptr;
299 size_t pending_tiles_ = 0;
300
301 size_t ensureScratch(size_t num_tiles, MemoryPool* pool, fz::stream_t stream);
302 void releaseScratch();
303
304 size_t numTiles(size_t n) const {
305 const size_t t = getTileSize();
306 return (n + t - 1) / t;
307 }
308
309 void validate() const {
310 if (config_.coder_block_size != 32)
311 throw std::invalid_argument(
312 "AdaptiveLorenzoStage: coder_block_size must be 32 (the cost model "
313 "and the per-block warp reduction both assume a 32-element block)");
314 if (config_.blocks_per_tile < 1 || config_.blocks_per_tile > 32)
315 throw std::invalid_argument(
316 "AdaptiveLorenzoStage: blocks_per_tile must be in [1, 32] so the "
317 "tile fits one CUDA block, got "
318 + std::to_string(config_.blocks_per_tile));
319 }
320
321 static DataType getElementDataType() { return fused::dataTypeOf<T>(); }
322};
323
324extern template class AdaptiveLorenzoStage<int16_t>;
325extern template class AdaptiveLorenzoStage<int32_t>;
326
328template<typename T>
330 const T* d_input, T* d_residuals, uint8_t* d_modes, T* d_means,
331 uint32_t* d_flags, size_t n, uint32_t tile_size, bool enable_order2,
332 bool enable_centering, EncodingOracleKind oracle_kind, fz::stream_t stream);
333
337template<typename T>
339 const T* d_residuals, const uint8_t* d_modes, const T* d_means,
340 const uint32_t* d_offsets, T* d_output,
341 size_t n, uint32_t tile_size, fz::stream_t stream);
342
345template<typename T>
347 const uint8_t* d_modes_dense, const T* d_means_dense, const uint32_t* d_offsets,
348 uint8_t* d_modes_packed, T* d_means_compact, size_t num_tiles,
349 fz::stream_t stream);
350
354 const uint8_t* d_modes_packed, uint32_t* d_flags, size_t num_tiles,
355 fz::stream_t stream);
356
357} // namespace fz
Definition adaptive_lorenzo_stage.h:98
uint16_t getStageTypeId() const override
Definition adaptive_lorenzo_stage.h:237
uint8_t getInputDataType(size_t input_index) const override
Definition adaptive_lorenzo_stage.h:246
size_t estimateScratchBytes(const std::vector< size_t > &input_sizes) const override
Defined in the .cu — sizing the CUB scan temp needs a CUDA translation unit.
size_t serializeHeader(size_t, uint8_t *buf, size_t max_size) const override
Definition adaptive_lorenzo_stage.h:251
size_t getMaxHeaderSize(size_t) const override
Definition adaptive_lorenzo_stage.h:278
bool isGraphCompatible() const override
Definition adaptive_lorenzo_stage.h:193
std::vector< FusedAuxOutputDecl > getFusedAuxOutputs() const override
Definition adaptive_lorenzo_stage.h:156
std::unordered_map< std::string, size_t > getActualOutputSizesByName() const override
Definition adaptive_lorenzo_stage.h:218
bool bindDownstreamEncodingOracle(const EncodingOracleDecl &decl) override
Definition adaptive_lorenzo_stage.h:131
void setInverse(bool inv) override
Definition adaptive_lorenzo_stage.h:121
void deserializeHeader(const uint8_t *buf, size_t size) override
Definition adaptive_lorenzo_stage.h:265
std::string getName() const override
Definition adaptive_lorenzo_stage.h:176
uint8_t getOutputDataType(size_t output_index) const override
Definition adaptive_lorenzo_stage.h:241
FusionSpec getFusionSpec() const override
Definition adaptive_lorenzo_stage.h:150
std::vector< std::string > getOutputNames() const override
Definition adaptive_lorenzo_stage.h:180
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
size_t getActualOutputSize(int index) const override
Definition adaptive_lorenzo_stage.h:226
void postStreamSync(fz::stream_t stream) override
void saveState() override
Definition adaptive_lorenzo_stage.h:212
void setFusedSideOutput(int output_index, size_t bytes) override
Definition adaptive_lorenzo_stage.h:231
std::vector< size_t > estimateOutputSizes(const std::vector< size_t > &input_sizes) const override
Definition adaptive_lorenzo_stage.h:200
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
void launchAdaptiveLorenzoFlags(const uint8_t *d_modes_packed, uint32_t *d_flags, size_t num_tiles, fz::stream_t stream)
constexpr size_t FZM_STAGE_CONFIG_SIZE
Per-stage serialized config slot (bytes)
Definition fzm_format.h:65
void launchAdaptiveLorenzoInverse(const T *d_residuals, const uint8_t *d_modes, const T *d_means, const uint32_t *d_offsets, T *d_output, size_t n, uint32_t tile_size, fz::stream_t stream)
@ ADAPTIVE_LORENZO
Per-tile adaptive multi-order Lorenzo + centering (FSZ prediction stage)
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)
void launchAdaptiveLorenzoCompact(const uint8_t *d_modes_dense, const T *d_means_dense, const uint32_t *d_offsets, uint8_t *d_modes_packed, T *d_means_compact, size_t num_tiles, fz::stream_t stream)
void launchAdaptiveLorenzoForward(const T *d_input, T *d_residuals, uint8_t *d_modes, T *d_means, uint32_t *d_flags, size_t n, uint32_t tile_size, bool enable_order2, bool enable_centering, EncodingOracleKind oracle_kind, fz::stream_t stream)
Forward: select the best variant per tile and emit its residuals.
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.
Serialized config stored in FZMBufferEntry.stage_config.
Definition adaptive_lorenzo_stage.h:27
uint8_t reserved[3]
Must be zero.
Definition adaptive_lorenzo_stage.h:33
uint8_t enable_order2
1 if LZ2 is a candidate variant.
Definition adaptive_lorenzo_stage.h:31
uint8_t blocks_per_tile
Coder blocks per adaptation tile.
Definition adaptive_lorenzo_stage.h:30
DataType data_type
Signed integer element type (1B).
Definition adaptive_lorenzo_stage.h:28
uint8_t coder_block_size
Downstream coder's block size (fixed at 32).
Definition adaptive_lorenzo_stage.h:29
uint8_t enable_centering
1 if centering is a candidate variant.
Definition adaptive_lorenzo_stage.h:32
uint32_t num_elements
Element count (for tile-count recovery).
Definition adaptive_lorenzo_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
Definition fusion.h:131
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.