88 void setInverse(
bool inverse)
override { is_inverse_ = inverse; }
89 bool isInverse()
const override {
return is_inverse_; }
94 const std::vector<void*>& inputs,
95 const std::vector<void*>& outputs,
96 const std::vector<size_t>& sizes
106 std::string
getName()
const override {
return "RLE"; }
107 size_t getNumInputs()
const override {
return 1; }
108 size_t getNumOutputs()
const override {
return 1; }
116 chunk_size_ =
static_cast<uint32_t
>(bytes - (bytes %
sizeof(T)));
118 size_t getChunkSize()
const {
return chunk_size_; }
119 bool isChunked()
const {
return chunk_size_ >=
sizeof(T); }
123 return isChunked() ? chunk_size_ : 1;
137 const std::vector<size_t>& input_sizes
139 if (is_inverse_ || input_sizes.empty())
return 0;
140 const size_t n = input_sizes[0] /
sizeof(T);
146 return n * (1 + 4 + 4 +
sizeof(T) + 4);
150 const std::vector<size_t>& input_sizes
156 if (cached_num_elements_ > 0)
157 return {
static_cast<size_t>(cached_num_elements_) *
sizeof(T)};
158 return {input_sizes[0] * 2};
165 size_t n = input_sizes[0] /
sizeof(T);
166 size_t values_bytes = n *
sizeof(T);
167 size_t values_aligned = (values_bytes + 3u) & ~3u;
170 const size_t nc = numChunks(input_sizes[0]);
171 return {rleChunkedValuesOffset<T>(nc) + values_aligned
172 + n *
sizeof(uint32_t)};
174 return {rleValuesOffset<T>() + values_aligned + n *
sizeof(uint32_t)};
179 completePendingSync();
180 completePendingDecodeSync();
181 return {{
"output", actual_output_sizes_.empty() ? 0 : actual_output_sizes_[0]}};
184 completePendingSync();
185 completePendingDecodeSync();
186 return (index == 0 && !actual_output_sizes_.empty()) ? actual_output_sizes_[0] : 0;
190 return static_cast<uint16_t
>(StageType::RLE);
195 return static_cast<uint8_t
>(getDataTypeEnum());
199 return static_cast<uint8_t
>(getDataTypeEnum());
202 size_t serializeHeader(
size_t output_index, uint8_t* header_buffer,
size_t max_size)
const override {
204 const size_t needed =
sizeof(
DataType) + 2 *
sizeof(uint32_t);
205 if (max_size < needed)
return 0;
207 std::memcpy(header_buffer, &dt,
sizeof(
DataType));
208 std::memcpy(header_buffer +
sizeof(
DataType), &cached_num_elements_,
sizeof(uint32_t));
209 std::memcpy(header_buffer +
sizeof(
DataType) +
sizeof(uint32_t),
210 &chunk_size_,
sizeof(uint32_t));
215 if (size >=
sizeof(
DataType) +
sizeof(uint32_t))
216 std::memcpy(&cached_num_elements_, header_buffer +
sizeof(
DataType),
sizeof(uint32_t));
219 if (size >=
sizeof(
DataType) + 2 *
sizeof(uint32_t))
220 std::memcpy(&chunk_size_, header_buffer +
sizeof(
DataType) +
sizeof(uint32_t),
226 return sizeof(
DataType) + 2 *
sizeof(uint32_t);
233 uint32_t chunk_size_ = 0;
236 uint32_t elemsPerChunk()
const {
237 return static_cast<uint32_t
>(chunk_size_ /
sizeof(T));
239 size_t numChunks(
size_t in_bytes)
const {
240 const size_t epc = elemsPerChunk();
241 const size_t n = in_bytes /
sizeof(T);
242 return epc ? (n + epc - 1) / epc : 0;
248 uint32_t cached_num_elements_ = 0;
252 uint8_t* d_is_boundary_ =
nullptr;
253 uint32_t* d_boundary_scan_ =
nullptr;
254 uint32_t* d_boundary_positions_ =
nullptr;
255 T* d_values_scratch_ =
nullptr;
256 uint32_t* d_lengths_scratch_ =
nullptr;
257 size_t fwd_scratch_n_ = 0;
258 MemoryPool* fwd_scratch_pool_ =
nullptr;
259 bool fwd_from_pool_ =
false;
260 std::weak_ptr<const void> fwd_scratch_alive_;
265 mutable uint32_t* h_num_runs_ =
nullptr;
266 mutable MemoryPool* h_num_runs_pool_ =
nullptr;
267 mutable std::weak_ptr<const void> h_num_runs_alive_;
268 mutable bool fwd_sync_pending_ =
false;
269 mutable fz::stream_t fwd_last_stream_ =
nullptr;
270 mutable std::vector<size_t> actual_output_sizes_;
278 mutable uint32_t* h_dec_total_size_ =
nullptr;
279 mutable MemoryPool* h_dec_total_size_pool_ =
nullptr;
280 mutable std::weak_ptr<const void> h_dec_total_size_alive_;
281 mutable bool dec_sync_pending_ =
false;
282 mutable fz::stream_t dec_last_stream_ =
nullptr;
284 void completePendingDecodeSync()
const {
285 if (!dec_sync_pending_)
return;
286 cudaStreamSynchronize(dec_last_stream_);
287 const uint32_t total_output_size = *h_dec_total_size_;
288 actual_output_sizes_ = {
static_cast<size_t>(total_output_size) *
sizeof(T)};
289 dec_sync_pending_ =
false;
295 void completePendingSync()
const {
296 if (!fwd_sync_pending_)
return;
297 cudaStreamSynchronize(fwd_last_stream_);
298 const uint32_t num_runs = *h_num_runs_;
299 const size_t values_bytes =
static_cast<size_t>(num_runs) *
sizeof(T);
300 const size_t values_aligned = (values_bytes + 3) & ~3;
301 const size_t values_offset = isChunked()
302 ? rleChunkedValuesOffset<T>(
303 numChunks(
static_cast<size_t>(cached_num_elements_) *
sizeof(T)))
305 actual_output_sizes_ = {
306 values_offset + values_aligned + num_runs *
sizeof(uint32_t)
308 fwd_sync_pending_ =
false;
310 const size_t in_bytes =
static_cast<size_t>(cached_num_elements_) *
sizeof(T);
311 const size_t out_bytes = actual_output_sizes_[0];
312 const float ratio = in_bytes > 0
313 ?
static_cast<float>(in_bytes) /
static_cast<float>(out_bytes) : 0.0f;
314 FZ_LOG(
DEBUG,
"RLE encode: %u runs / %u elems %.1f KB -> %.1f KB ratio %.2fx",
315 num_runs, cached_num_elements_,
316 in_bytes / 1024.0f, out_bytes / 1024.0f, ratio);
321 if (std::is_same<T, uint8_t>::value)
return DataType::UINT8;
322 if (std::is_same<T, uint16_t>::value)
return DataType::UINT16;
323 if (std::is_same<T, uint32_t>::value)
return DataType::UINT32;
324 if (std::is_same<T, uint64_t>::value)
return DataType::UINT64;
325 if (std::is_same<T, int8_t>::value)
return DataType::INT8;
326 if (std::is_same<T, int16_t>::value)
return DataType::INT16;
327 if (std::is_same<T, int32_t>::value)
return DataType::INT32;
328 if (std::is_same<T, int64_t>::value)
return DataType::INT64;
329 if (std::is_same<T, float>::value)
return DataType::FLOAT32;
330 if (std::is_same<T, double>::value)
return DataType::FLOAT64;
331 return DataType::UINT8;