FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
GpuANSDecode.h
1
10#ifndef FZ_ANS_DIETGPU_ANS_GPUANSDECODE_H
11#define FZ_ANS_DIETGPU_ANS_GPUANSDECODE_H
12
13#pragma once
14
15#include "GpuANSCodec.h"
16#include "utils/DeviceUtils.h"
17#include "utils/PtxUtils.h"
18#include "utils/StaticUtils.h"
19#include <cmath>
20#include <cub/block/block_scan.cuh>
21#include <memory>
22
23namespace fz { namespace ans {
24
25// We are limited to 11 bits of probability resolution
26// (worst case, prec = 12, pdf == 2^12, single symbol. 2^12 cannot be
27// represented in 12 bits)
28inline __device__ uint32_t
29packDecodeLookup(uint32_t sym, uint32_t pdf, uint32_t cdf) {
30 static_assert(sizeof(ANSDecodedT) == 1, "");
31 // [31:20] cdf
32 // [19:8] pdf
33 // [7:0] symbol
34 return (cdf << 20) | (pdf << 8) | sym;
35}
36
37inline __device__ void
38unpackDecodeLookup(uint32_t v, uint32_t& sym, uint32_t& pdf, uint32_t& cdf) {
39 // [31:20] cdf
40 // [19:8] pdf
41 // [7:0] symbol
42 sym = v & 0xffU;
43 v >>= 8;
44 pdf = v & 0xfffU;
45 v >>= 12;
46 cdf = v;
47}
48
49template <int ProbBits>
50__device__ void decodeOneWarp(
51 ANSStateT& state,
52 uint32_t compressedOffset,
53 const ANSEncodedT* __restrict__ in,
54 const uint32_t* lookup,
55 uint32_t& outNumRead,
56 ANSDecodedT& outSym) {
57 constexpr ANSStateT StateMask = (ANSStateT(1) << ProbBits) - ANSStateT(1);
58
59 auto s_bar = state & StateMask;
60
61 uint32_t sym;
62 uint32_t pdf;
63 uint32_t sMinusCdf;
64 unpackDecodeLookup(lookup[s_bar], sym, pdf, sMinusCdf);
65
66 outSym = sym;
67 state = pdf * (state >> ProbBits) + ANSStateT(sMinusCdf);
68
69 bool read = state < kANSMinState;
70 auto vote = __ballot_sync(0xffffffff, read);
71 auto prefix = __popc(vote & getLaneMaskGe());
72
73 if (read) {
74 auto v = in[-prefix];
75 state = (state << kANSEncodedBits) + ANSStateT(v);
76 }
77
78 outNumRead = __popc(vote);
79}
80
81template <int ProbBits>
82__device__ void decodeOnePartialWarp(
83 bool valid,
84 ANSStateT& state,
85 uint32_t compressedOffset,
86 const ANSEncodedT* __restrict__ in,
87 const uint32_t* lookup,
88 uint32_t& outNumRead,
89 ANSDecodedT& outSym) {
90 constexpr ANSStateT StateMask = (ANSStateT(1) << ProbBits) - ANSStateT(1);
91
92 auto s_bar = state & StateMask;
93
94 uint32_t sym;
95 uint32_t pdf;
96 uint32_t sMinusCdf;
97 unpackDecodeLookup(lookup[s_bar], sym, pdf, sMinusCdf);
98
99 if (valid) {
100 outSym = sym;
101 state = pdf * (state >> ProbBits) + ANSStateT(sMinusCdf);
102 }
103
104 bool read = valid && (state < kANSMinState);
105 auto vote = __ballot_sync(0xffffffff, read);
106 auto prefix = __popc(vote & getLaneMaskGe());
107
108 if (read) {
109 auto v = in[-prefix];
110 state = (state << kANSEncodedBits) + ANSStateT(v);
111 }
112
113 outNumRead = __popc(vote);
114}
115
116template <int ProbBits>
117__device__ void ansDecodeWarpBlock(
118 int laneId,
119 ANSStateT state,
120 uint32_t uncompressedWords,
121 uint32_t compressedWords,
122 const ANSEncodedT* __restrict__ in,
123 BatchWriter& writer,
124 const uint32_t* __restrict__ table) {
125 uint32_t remainder = uncompressedWords % kWarpSize;
126
127 int uncompressedOffset = uncompressedWords - remainder;
128
129 uint32_t compressedOffset = compressedWords;
130
131 in += compressedOffset;
132
133 if (remainder) {
134 bool valid = laneId < remainder;
135
136 uint32_t numCompressedRead;
137 ANSDecodedT sym;
138
139 decodeOnePartialWarp<ProbBits>(
140 valid, state, compressedOffset, in, table, numCompressedRead, sym);
141
142 if (valid) {
143 writer.write(uncompressedOffset + laneId, sym);
144 }
145
146 in -= numCompressedRead;
147 }
148
149 while (uncompressedOffset > 0) {
150 uncompressedOffset -= kWarpSize;
151
152 uint32_t numCompressedRead;
153 ANSDecodedT sym;
154
155 decodeOneWarp<ProbBits>(
156 state, compressedOffset, in, table, numCompressedRead, sym);
157
158 writer.write(uncompressedOffset + laneId, sym);
159
160 in -= numCompressedRead;
161 }
162}
163
164template <
165 int Threads,
166 int ProbBits,
167 int BlockSize>
168__global__ __launch_bounds__(128) void ansDecodeKernel(
169 void* in,
170 uint32_t* table,
171 void* out) {
172 int tid = threadIdx.x;
173
174 auto headerIn = (const ANSCoalescedHeader*)in;
175 headerIn->checkMagicAndVersion();
176
177 auto header = *headerIn;
178 auto numBlocks = header.getNumBlocks();
179 auto totalUncompressedWords = header.getTotalUncompressedWords();
180
181 assert(ProbBits == header.getProbBits());
182
183 constexpr int kBuckets = 1 << ProbBits;
184 __shared__ uint32_t lookup[kBuckets];
185
186 {
187 uint4* lookup4 = (uint4*)lookup;
188 const uint4* table4 = (const uint4*)table;
189
190 static_assert(isEvenDivisor(kBuckets, Threads * 4), "");
191 for (int j = 0;
192 j < kBuckets / (Threads * (sizeof(uint4) / sizeof(uint32_t)));
193 ++j) {
194 lookup4[j * Threads + tid] = table4[j * Threads + tid];
195 }
196 }
197
198 __syncthreads();
199
200 auto writer = BatchWriter(out);
201
202 int globalWarpId =
203 __shfl_sync(0xffffffff, (blockIdx.x * blockDim.x + tid) / kWarpSize, 0);
204
205 int warpsPerGrid = gridDim.x * Threads / kWarpSize;
206 int laneId = getLaneId();
207
208 for (int block = globalWarpId; block < numBlocks; block += warpsPerGrid) {
209 ANSStateT state = headerIn->getWarpStates()[block].warpState[laneId];
210
211 auto blockWords = headerIn->getBlockWords(numBlocks)[block];
212 uint32_t uncompressedWords = (blockWords.x >> 16);
213 uint32_t compressedWords = (blockWords.x & 0xffff);
214 uint32_t blockCompressedWordStart = blockWords.y;
215
216 auto blockDataIn =
217 headerIn->getBlockDataStart(numBlocks) + blockCompressedWordStart;
218
219 writer.setBlock(block);
220
221 if (uncompressedWords == BlockSize) {
222 blockDataIn += compressedWords;
223
224 for (int i = BlockSize - kWarpSize + laneId; i >= 0; i -= kWarpSize) {
225 ANSDecodedT sym;
226 uint32_t numCompressedRead;
227
228 decodeOneWarp<ProbBits>(
229 state, compressedWords, blockDataIn, lookup, numCompressedRead, sym);
230
231 blockDataIn -= numCompressedRead;
232
233 writer.write(i, sym);
234 }
235 } else {
236 ansDecodeWarpBlock<ProbBits>(
237 laneId,
238 state,
239 uncompressedWords,
240 compressedWords,
241 blockDataIn,
242 writer,
243 lookup);
244 }
245 }
246}
247
248template <int Threads>
249__global__ void ansDecodeTable(
250 void* in,
251 uint32_t probBits,
252 uint32_t* __restrict__ table) {
253 int tid = threadIdx.x;
254 int warpId = tid / kWarpSize;
255 int laneId = getLaneId();
256
257 auto headerIn = (const ANSCoalescedHeader*)in;
258
259 auto header = *headerIn;
260
261 if (header.getTotalUncompressedWords() == 0) {
262 return;
263 }
264
265 auto probs = headerIn->getSymbolProbs();
266
267 uint32_t pdf = tid < kNumSymbols ? probs[tid] : 0;
268 uint32_t cdf = 0;
269
270 using BlockScan = cub::BlockScan<uint32_t, Threads>;
271 __shared__ typename BlockScan::TempStorage tempStorage;
272
273 uint32_t total = 0;
274 BlockScan(tempStorage).ExclusiveSum(pdf, cdf, total);
275
276 __shared__ uint2 smemPdfCdf[kNumSymbols];
277
278 if (tid < kNumSymbols) {
279 smemPdfCdf[tid] = uint2{pdf, cdf};
280 }
281
282 __syncthreads();
283
284 constexpr int kWarpsPerBlock = Threads / kWarpSize;
285
286 for (int i = warpId; i < kNumSymbols; i += kWarpsPerBlock) {
287 auto v = smemPdfCdf[i];
288
289 auto pdf = v.x;
290 auto begin = v.y;
291 auto end = begin + pdf;
292
293 for (int j = begin + laneId; j < end; j += kWarpSize) {
294 table[j] = packDecodeLookup(
295 i, // symbol
296 pdf, // bucket pdf
297 j - begin); // within-bucket cdf
298 }
299 }
300}
301
302}} // namespace fz::ans
303
304#endif // FZ_ANS_DIETGPU_ANS_GPUANSDECODE_H
Definition dag.h:24