10#ifndef FZ_ANS_DIETGPU_ANS_GPUANSDECODE_H
11#define FZ_ANS_DIETGPU_ANS_GPUANSDECODE_H
15#include "GpuANSCodec.h"
16#include "utils/DeviceUtils.h"
17#include "utils/PtxUtils.h"
18#include "utils/StaticUtils.h"
20#include <cub/block/block_scan.cuh>
23namespace fz {
namespace ans {
28inline __device__ uint32_t
29packDecodeLookup(uint32_t sym, uint32_t pdf, uint32_t cdf) {
30 static_assert(
sizeof(ANSDecodedT) == 1,
"");
34 return (cdf << 20) | (pdf << 8) | sym;
38unpackDecodeLookup(uint32_t v, uint32_t& sym, uint32_t& pdf, uint32_t& cdf) {
49template <
int ProbBits>
50__device__
void decodeOneWarp(
52 uint32_t compressedOffset,
53 const ANSEncodedT* __restrict__ in,
54 const uint32_t* lookup,
56 ANSDecodedT& outSym) {
57 constexpr ANSStateT StateMask = (ANSStateT(1) << ProbBits) - ANSStateT(1);
59 auto s_bar = state & StateMask;
64 unpackDecodeLookup(lookup[s_bar], sym, pdf, sMinusCdf);
67 state = pdf * (state >> ProbBits) + ANSStateT(sMinusCdf);
69 bool read = state < kANSMinState;
70 auto vote = __ballot_sync(0xffffffff, read);
71 auto prefix = __popc(vote & getLaneMaskGe());
75 state = (state << kANSEncodedBits) + ANSStateT(v);
78 outNumRead = __popc(vote);
81template <
int ProbBits>
82__device__
void decodeOnePartialWarp(
85 uint32_t compressedOffset,
86 const ANSEncodedT* __restrict__ in,
87 const uint32_t* lookup,
89 ANSDecodedT& outSym) {
90 constexpr ANSStateT StateMask = (ANSStateT(1) << ProbBits) - ANSStateT(1);
92 auto s_bar = state & StateMask;
97 unpackDecodeLookup(lookup[s_bar], sym, pdf, sMinusCdf);
101 state = pdf * (state >> ProbBits) + ANSStateT(sMinusCdf);
104 bool read = valid && (state < kANSMinState);
105 auto vote = __ballot_sync(0xffffffff, read);
106 auto prefix = __popc(vote & getLaneMaskGe());
109 auto v = in[-prefix];
110 state = (state << kANSEncodedBits) + ANSStateT(v);
113 outNumRead = __popc(vote);
116template <
int ProbBits>
117__device__
void ansDecodeWarpBlock(
120 uint32_t uncompressedWords,
121 uint32_t compressedWords,
122 const ANSEncodedT* __restrict__ in,
124 const uint32_t* __restrict__ table) {
125 uint32_t remainder = uncompressedWords % kWarpSize;
127 int uncompressedOffset = uncompressedWords - remainder;
129 uint32_t compressedOffset = compressedWords;
131 in += compressedOffset;
134 bool valid = laneId < remainder;
136 uint32_t numCompressedRead;
139 decodeOnePartialWarp<ProbBits>(
140 valid, state, compressedOffset, in, table, numCompressedRead, sym);
143 writer.write(uncompressedOffset + laneId, sym);
146 in -= numCompressedRead;
149 while (uncompressedOffset > 0) {
150 uncompressedOffset -= kWarpSize;
152 uint32_t numCompressedRead;
155 decodeOneWarp<ProbBits>(
156 state, compressedOffset, in, table, numCompressedRead, sym);
158 writer.write(uncompressedOffset + laneId, sym);
160 in -= numCompressedRead;
168__global__ __launch_bounds__(128) void ansDecodeKernel(
172 int tid = threadIdx.x;
174 auto headerIn = (
const ANSCoalescedHeader*)in;
175 headerIn->checkMagicAndVersion();
177 auto header = *headerIn;
178 auto numBlocks = header.getNumBlocks();
179 auto totalUncompressedWords = header.getTotalUncompressedWords();
181 assert(ProbBits == header.getProbBits());
183 constexpr int kBuckets = 1 << ProbBits;
184 __shared__ uint32_t lookup[kBuckets];
187 uint4* lookup4 = (uint4*)lookup;
188 const uint4* table4 = (
const uint4*)table;
190 static_assert(isEvenDivisor(kBuckets, Threads * 4),
"");
192 j < kBuckets / (Threads * (
sizeof(uint4) /
sizeof(uint32_t)));
194 lookup4[j * Threads + tid] = table4[j * Threads + tid];
200 auto writer = BatchWriter(out);
203 __shfl_sync(0xffffffff, (blockIdx.x * blockDim.x + tid) / kWarpSize, 0);
205 int warpsPerGrid = gridDim.x * Threads / kWarpSize;
206 int laneId = getLaneId();
208 for (
int block = globalWarpId; block < numBlocks; block += warpsPerGrid) {
209 ANSStateT state = headerIn->getWarpStates()[block].warpState[laneId];
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;
217 headerIn->getBlockDataStart(numBlocks) + blockCompressedWordStart;
219 writer.setBlock(block);
221 if (uncompressedWords == BlockSize) {
222 blockDataIn += compressedWords;
224 for (
int i = BlockSize - kWarpSize + laneId; i >= 0; i -= kWarpSize) {
226 uint32_t numCompressedRead;
228 decodeOneWarp<ProbBits>(
229 state, compressedWords, blockDataIn, lookup, numCompressedRead, sym);
231 blockDataIn -= numCompressedRead;
233 writer.write(i, sym);
236 ansDecodeWarpBlock<ProbBits>(
248template <
int Threads>
249__global__
void ansDecodeTable(
252 uint32_t* __restrict__ table) {
253 int tid = threadIdx.x;
254 int warpId = tid / kWarpSize;
255 int laneId = getLaneId();
257 auto headerIn = (
const ANSCoalescedHeader*)in;
259 auto header = *headerIn;
261 if (header.getTotalUncompressedWords() == 0) {
265 auto probs = headerIn->getSymbolProbs();
267 uint32_t pdf = tid < kNumSymbols ? probs[tid] : 0;
270 using BlockScan = cub::BlockScan<uint32_t, Threads>;
271 __shared__
typename BlockScan::TempStorage tempStorage;
274 BlockScan(tempStorage).ExclusiveSum(pdf, cdf, total);
276 __shared__ uint2 smemPdfCdf[kNumSymbols];
278 if (tid < kNumSymbols) {
279 smemPdfCdf[tid] = uint2{pdf, cdf};
284 constexpr int kWarpsPerBlock = Threads / kWarpSize;
286 for (
int i = warpId; i < kNumSymbols; i += kWarpsPerBlock) {
287 auto v = smemPdfCdf[i];
291 auto end = begin + pdf;
293 for (
int j = begin + laneId; j < end; j += kWarpSize) {
294 table[j] = packDecodeLookup(