FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
golomb_rice_stage.h
Go to the documentation of this file.
1#pragma once
2
100#include "stage/stage.h"
101#include "fzm_format.h"
102#include "backend/types.h"
104#include <cstdint>
105#include <cstring>
106#include <memory>
107#include <stdexcept>
108#include <string>
109#include <type_traits>
110#include <unordered_map>
111#include <vector>
112
113namespace fz {
114
115template<typename T>
116class GolombRiceStage : public Stage {
117 static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
118 "GolombRiceStage requires a signed integer type");
119public:
120 GolombRiceStage() = default;
121 ~GolombRiceStage() override;
122
123 // ── Stage control ──────────────────────────────────────────────────────
124 void setInverse(bool inv) override { is_inverse_ = inv; }
125 bool isInverse() const override { return is_inverse_; }
126
130 bool isGraphCompatible() const override { return !is_inverse_; }
131
132 void setChunkSize(size_t bytes) { chunk_size_ = static_cast<uint32_t>(bytes); }
133 size_t getChunkSize() const { return chunk_size_; }
134 size_t getRequiredInputAlignment() const override { return chunk_size_; }
135 uint32_t getCachedOrigBytes() const { return cached_orig_bytes_; }
136
144 FusionSpec getFusionSpec() const override {
145 if (is_inverse_ || chunk_size_ != 16384u || !std::is_same<T, int32_t>::value)
146 return {};
147 return FusionSpec{FusionAccess::SegmentCodec, chunk_size_};
148 }
149
151 FusedOpDecl getFusedOp() const override {
152 if (!getFusionSpec().fusable()) return {};
153 return FusedOpDecl{FusionStrategy::ChunkCooperative, "GolombRiceCoder",
154 "fused/chunk_fusion/chunk_fusion.cuh", {}};
155 }
157 void setFusedArchiveResult(size_t archive_bytes, size_t orig_bytes) override {
158 setFusedResult(archive_bytes, orig_bytes);
159 }
165 void setFusedResult(size_t archive_bytes, size_t orig_bytes) {
166 actual_output_size_ = archive_bytes;
167 cached_orig_bytes_ = static_cast<uint32_t>(orig_bytes);
168 tail_readback_pending_ = false;
169 }
170
171 // ── Execution ──────────────────────────────────────────────────────────
172 void execute(
173 fz::stream_t stream,
174 MemoryPool* pool,
175 const std::vector<void*>& inputs,
176 const std::vector<void*>& outputs,
177 const std::vector<size_t>& sizes
178 ) override;
179 void postStreamSync(fz::stream_t stream) override;
180
181 // ── Metadata ───────────────────────────────────────────────────────────
182 std::string getName() const override { return "GolombRice"; }
183 size_t getNumInputs() const override { return 1; }
184 size_t getNumOutputs() const override { return 1; }
185
186 std::vector<size_t> estimateOutputSizes(
187 const std::vector<size_t>& input_sizes
188 ) const override {
189 if (is_inverse_) {
190 if (cached_orig_bytes_ > 0)
191 return {static_cast<size_t>(cached_orig_bytes_)};
192 return {input_sizes.empty() ? 0 : input_sizes[0]};
193 }
194 // Forward worst case: every chunk falls back to raw storage (original
195 // bytes + 1 flagged size word each) plus the stream header, plus the
196 // tail safety pad postStreamSync may need to zero (see execute()).
197 const size_t n_bytes = input_sizes.empty() ? 0 : input_sizes[0];
198 const size_t n_chunks = (n_bytes + chunk_size_ - 1) / chunk_size_;
199 const size_t hdr = 4 + 4 + 4 * n_chunks;
200 const size_t worst = n_bytes + hdr + kTailPad;
201 return {(worst + 3) & ~size_t(3)};
202 }
203
204 std::unordered_map<std::string, size_t>
205 getActualOutputSizesByName() const override {
206 return {{"output", actual_output_size_}};
207 }
208 size_t getActualOutputSize(int index) const override {
209 return index == 0 ? actual_output_size_ : 0;
210 }
211
223 size_t estimateScratchBytes(
224 const std::vector<size_t>& input_sizes
225 ) const override {
226 if (is_inverse_ || input_sizes.empty()) return 0;
227 const size_t in_bytes = input_sizes[0];
228 const size_t n_chunks = (in_bytes + chunk_size_ - 1) / chunk_size_;
229 return n_chunks * (chunkScratchStride() + 3 * sizeof(uint32_t));
230 }
231
232 uint16_t getStageTypeId() const override {
233 return static_cast<uint16_t>(StageType::GOLOMB_RICE);
234 }
235
236 uint8_t getOutputDataType(size_t) const override {
237 return static_cast<uint8_t>(DataType::UINT8);
238 }
239
240 // ── Serialization ──────────────────────────────────────────────────────
241 static DataType getElementDataType() { return fused::dataTypeOf<T>(); }
242
243 size_t serializeHeader(size_t, uint8_t* buf, size_t max_size) const override {
244 if (max_size < 5) return 0;
245 std::memcpy(buf, &chunk_size_, sizeof(uint32_t));
246 buf[4] = static_cast<uint8_t>(getElementDataType());
247 return 5;
248 }
249 void deserializeHeader(const uint8_t* buf, size_t size) override {
250 if (size >= 4) std::memcpy(&chunk_size_, buf, sizeof(uint32_t));
251 }
252 size_t getMaxHeaderSize(size_t) const override { return 5; }
253
254 void saveState() override { saved_chunk_size_ = chunk_size_; }
255 void restoreState() override { chunk_size_ = saved_chunk_size_; }
256
258 uint32_t elemsPerChunk() const { return chunk_size_ / static_cast<uint32_t>(sizeof(T)); }
259
263 static constexpr uint32_t kEscapeQ = 24;
264 static constexpr uint32_t bitWidth() { return 8u * sizeof(T); }
266 static constexpr uint32_t kMaxCandidate() { return bitWidth() > 24 ? 24u : bitWidth() - 1; }
267
286 static constexpr uint32_t kIntervalsPerChunk = 16;
287
298 size_t chunkScratchStride() const {
299 const size_t worst_bits = static_cast<size_t>(elemsPerChunk()) * (kEscapeQ + bitWidth());
300 const size_t worst_bytes = (4 + 4 * kIntervalsPerChunk) /*k + offset table*/
301 + kIntervalsPerChunk /*per-interval rounding slack*/
302 + (worst_bits + 7) / 8;
303 return ((worst_bytes + 3) & ~size_t(3)) + kTailPad;
304 }
305 static constexpr size_t kTailPad = 16;
306
307private:
308 bool is_inverse_ = false;
309 uint32_t chunk_size_ = 16384;
310 uint32_t saved_chunk_size_ = 0;
311 size_t actual_output_size_ = 0;
312 uint32_t cached_orig_bytes_ = 0;
313 uint32_t saved_cached_orig_bytes_ = 0;
314
315 // ── Persistent forward scratch buffers ───────────────────────────────────
316 uint8_t* d_scratch_ = nullptr;
317 uint32_t* d_sizes_dev_ = nullptr;
318 uint32_t* d_dst_off_dev_ = nullptr;
319 mutable bool tail_readback_pending_ = false;
320 mutable uint32_t tail_last_index_ = 0;
321 mutable uint32_t tail_header_size_ = 0;
322 mutable uint8_t* tail_output_ptr_ = nullptr;
323 size_t scratch_capacity_ = 0;
324 MemoryPool* scratch_pool_owner_ = nullptr;
325 bool scratch_from_pool_ = false;
326 std::weak_ptr<const void> scratch_alive_;
327};
328
329} // namespace fz
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.
Definition dag.h:24
@ GOLOMB_RICE
Chunk-local Golomb-Rice entropy coder (exact per-chunk k, escape-bounded)
DataType
Element data type identifiers used in buffer and stage descriptors.
Definition fzm_format.h:142
Base class interface for all compression stages.
Backend-neutral GPU type aliases.