Files
paddlepaddle--paddle/paddle/phi/kernels/gpu/top_p_sampling_kernel.cu
T
2026-07-13 12:40:42 +08:00

1303 lines
43 KiB
Plaintext

// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/phi/kernels/top_p_sampling_kernel.h"
#ifdef PADDLE_WITH_HIP
#include <hip/hip_fp16.h>
#include <hip/hip_runtime.h>
#include <hiprand_kernel.h>
#else
#include <cuda_fp16.h>
#include <curand_kernel.h>
#endif
#include "paddle/phi/kernels/funcs/cub.h"
#if defined(__CUDACC__) && CUDA_VERSION >= 11060
#define CUDA_BFLOAT16_AVAILABLE
#include <cuda_bf16.h>
#endif
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/backends/gpu/gpu_device_function.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/kernels/funcs/gather.cu.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/top_k_function_cuda.h"
#include "paddle/phi/kernels/primitive/functor_primitives.h"
#ifdef PADDLE_WITH_HIP
#define GPU(str) hip##str
#else
#define GPU(str) cu##str
#endif
// #define DEBUG_TOPP
namespace phi {
template <typename T>
struct DataTypeTraits {
using DataType = T;
};
template <>
struct DataTypeTraits<phi::float16> {
using DataType = half;
};
#ifdef CUDA_BFLOAT16_AVAILABLE
template <>
struct DataTypeTraits<phi::bfloat16> {
using DataType = __nv_bfloat16;
};
#endif
#define FINAL_MASK 0xFFFFFFFF
#define FIXED_BLOCK_DIM_BASE(dim, ...) \
case (dim): { \
constexpr auto kBlockDim = (dim); \
__VA_ARGS__; \
} break
#ifdef PADDLE_WITH_HIP
#define WARP_SIZE 64
#define FIXED_BLOCK_DIM(...) \
FIXED_BLOCK_DIM_BASE(1024, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(512, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(256, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(128, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(64, ##__VA_ARGS__);
#else
#define WARP_SIZE 32
#define FIXED_BLOCK_DIM(...) \
FIXED_BLOCK_DIM_BASE(1024, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(512, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(256, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(128, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(64, ##__VA_ARGS__); \
FIXED_BLOCK_DIM_BASE(32, ##__VA_ARGS__)
#endif
struct SegmentOffsetIter {
explicit SegmentOffsetIter(int num_cols) : num_cols_(num_cols) {}
__host__ __device__ __forceinline__ int operator()(int idx) const {
#if !defined(__CUDA_ARCH__) && !defined(__HIP_DEVICE_COMPILE__)
PADDLE_ENFORCE_LE_INT_MAX(static_cast<int64_t>(idx) * num_cols_,
"idx * num_cols_");
#endif
return static_cast<int>(static_cast<int64_t>(idx) * num_cols_);
}
int num_cols_;
};
template <typename T>
struct Pair {
__device__ __forceinline__ Pair() {}
__device__ __forceinline__ Pair(T value, int id) : v(value), id(id) {}
__device__ __forceinline__ void set(T value, int id) {
this->v = value;
this->id = id;
}
__device__ __forceinline__ void operator=(const Pair<T>& in) {
v = in.v;
id = in.id;
}
__device__ __forceinline__ bool operator<(const T value) const {
return (static_cast<float>(v) < static_cast<float>(value));
}
__device__ __forceinline__ bool operator>(const T value) const {
return (static_cast<float>(v) > static_cast<float>(value));
}
__device__ __forceinline__ bool operator<(const Pair<T>& in) const {
return (static_cast<float>(v) < static_cast<float>(in.v)) ||
((static_cast<float>(v) == static_cast<float>(in.v)) &&
(id > in.id));
}
__device__ __forceinline__ bool operator>(const Pair<T>& in) const {
return (static_cast<float>(v) > static_cast<float>(in.v)) ||
((static_cast<float>(v) == static_cast<float>(in.v)) &&
(id < in.id));
}
T v;
int id;
};
int GetBlockSize(int vocab_size) {
if (vocab_size > 512) {
return 1024;
} else if (vocab_size > 256) {
return 512;
} else if (vocab_size > 128) {
return 256;
} else if (vocab_size > 64) {
return 128;
} else {
return 64;
}
}
inline int64_t div_up(int64_t a, int64_t n) { return (a + n - 1) / n; }
template <typename T>
__device__ __forceinline__ void AddTo(Pair<T> topk[],
const Pair<T>& p,
int beam_size) {
for (int k = beam_size - 2; k >= 0; k--) {
if (topk[k] < p) {
topk[k + 1] = topk[k];
} else {
topk[k + 1] = p;
return;
}
}
topk[0] = p;
}
template <typename T, int BlockSize>
__device__ __forceinline__ void GetTopK(
Pair<T> topk[], const T* src, int idx, int dim, int beam_size) {
while (idx < dim) {
if (topk[beam_size - 1] < src[idx]) {
Pair<T> tmp(src[idx], idx);
AddTo<T>(topk, tmp, beam_size);
}
idx += BlockSize;
}
}
template <typename T, int BlockSize>
__device__ __forceinline__ void GetTopK(Pair<T> topk[],
const T* src,
int idx,
int dim,
const Pair<T>& max,
int beam_size) {
while (idx < dim) {
if (topk[beam_size - 1] < src[idx]) {
Pair<T> tmp(src[idx], idx);
if (tmp < max) {
AddTo<T>(topk, tmp, beam_size);
}
}
idx += BlockSize;
}
}
template <typename T, int MaxLength, int BlockSize>
__device__ __forceinline__ void ThreadGetTopK(Pair<T> topk[],
int* beam,
int beam_size,
const T* src,
bool* firstStep,
bool* is_empty,
Pair<T>* max,
int dim,
const int tid) {
if (*beam > 0) {
int length = (*beam) < beam_size ? *beam : beam_size;
if (*firstStep) {
*firstStep = false;
GetTopK<T, BlockSize>(topk, src, tid, dim, length);
} else {
for (int k = 0; k < MaxLength; k++) {
if (k < MaxLength - (*beam)) {
topk[k] = topk[k + *beam];
} else {
topk[k].set(std::numeric_limits<T>::min(), -1);
}
}
if (!(*is_empty)) {
GetTopK<T, BlockSize>(
topk + MaxLength - *beam, src, tid, dim, *max, length);
}
}
*max = topk[MaxLength - 1];
if ((*max).id == -1) *is_empty = true;
*beam = 0;
}
}
template <typename T>
__forceinline__ __device__ Pair<T> WarpReduce(Pair<T> input) {
#pragma unroll
for (int offset = WARP_SIZE / 2; offset > 0; offset >>= 1) {
T tmp_val = backends::gpu::CudaShuffleDownSync(
FINAL_MASK, input.v, offset, WARP_SIZE);
int tmp_id = backends::gpu::CudaShuffleDownSync(
FINAL_MASK, input.id, offset, WARP_SIZE);
if (static_cast<float>(input.v) < static_cast<float>(tmp_val)) {
input.v = tmp_val;
input.id = tmp_id;
}
}
return input;
}
template <typename T, int MaxLength, int BlockSize>
__device__ __forceinline__ void BlockReduce(Pair<T> shared_max[],
Pair<T> topk[],
Pair<T> beam_max[],
int* beam,
int* k,
int* count,
const int tid,
const int wid,
const int lane) {
while (true) {
__syncthreads();
Pair<T> input_now = topk[0];
input_now = WarpReduce(input_now);
if (lane == 0) {
shared_max[wid] = input_now;
}
__syncthreads();
input_now = (tid < BlockSize / WARP_SIZE)
? shared_max[lane]
: Pair<T>(std::numeric_limits<T>::min(), -1);
if (wid == 0) {
input_now = WarpReduce(input_now);
if (lane == 0) shared_max[0] = input_now;
}
__syncthreads();
if (tid == 0) {
beam_max[*count] = shared_max[0];
(*count)++;
}
int tid_max = shared_max[0].id % BlockSize;
if (tid == tid_max) {
(*beam)++;
}
if (--(*k) == 0) break;
__syncthreads();
if (tid == tid_max) {
if (*beam < MaxLength) {
topk[0] = topk[*beam];
}
}
if (MaxLength < 5) {
if (*beam >= MaxLength) break;
} else {
#ifdef PADDLE_WITH_HIP
uint64_t mask = 0u;
mask = __ballot(true);
if (tid_max / WARP_SIZE == wid) {
if (__shfl_down(*beam, tid_max % WARP_SIZE, WARP_SIZE) == MaxLength)
break;
}
#else
unsigned mask = 0u;
mask = __ballot_sync(FINAL_MASK, true);
if (tid_max / WARP_SIZE == wid) {
if (__shfl_down_sync(
FINAL_MASK, *beam, tid_max % WARP_SIZE, WARP_SIZE) == MaxLength)
break;
}
#endif
}
}
}
template <typename T>
__device__ inline T exponential_transform(T val, T lambda) {
#if defined(__NVCC__) || defined(__HIPCC__)
T log = -std::numeric_limits<T>::epsilon() / 2;
if (val < static_cast<T>(1.) - std::numeric_limits<T>::epsilon() / 2) {
if (std::is_same<T, double>::value) {
log = logf(val);
} else {
log = __logf(val);
}
}
return static_cast<T>(-1.0) / lambda * log;
#else
return static_cast<T>(-1.0) / lambda * std::log(static_cast<T>(1.0) - val);
#endif
}
template <typename T, int MaxLength, int TopPBeamTopK, int BlockSize>
__global__ void KeMatrixTopPBeamTopK(const T* src,
const T* threshold,
GPU(randState_t) * states,
T* top_ps,
int64_t* out_id, // topk id
T* out_val, // topk val
int64_t* topk_ids,
T* topk_scores,
int vocab_size,
int* count_iter,
int* count_iter_begin,
const int k,
const bool need_batch_random) {
const int tid = threadIdx.x;
const int wid = tid / WARP_SIZE;
const int lane = tid % WARP_SIZE;
const int bid = blockIdx.x;
const float threshold_now =
threshold ? static_cast<float>(threshold[bid]) : 0.f;
int top_num = TopPBeamTopK;
float top_p_num = static_cast<float>(top_ps[bid]);
const int64_t offset = static_cast<int64_t>(bid) * vocab_size;
int64_t* topk_ids_now = nullptr;
T* topk_scores_now = nullptr;
if (k > 0) {
topk_ids_now = topk_ids + static_cast<int64_t>(bid) * k;
topk_scores_now = topk_scores + static_cast<int64_t>(bid) * k;
}
__shared__ Pair<T> shared_max[BlockSize / WARP_SIZE];
__shared__ Pair<T> beam_max[TopPBeamTopK];
Pair<T> topk[MaxLength];
int beam = MaxLength;
Pair<T> max;
bool is_empty = false;
bool firststep = true;
__shared__ int count;
if (tid == 0) {
count = 0;
}
for (int j = 0; j < MaxLength; j++) {
topk[j].set(std::numeric_limits<T>::min(), -1);
}
while (top_num) {
ThreadGetTopK<T, MaxLength, BlockSize>(topk,
&beam,
TopPBeamTopK,
src + offset,
&firststep,
&is_empty,
&max,
vocab_size,
tid);
BlockReduce<T, MaxLength, BlockSize>(
shared_max, topk, beam_max, &beam, &top_num, &count, tid, wid, lane);
}
if (tid == 0) {
count_iter_begin[bid] = count_iter[bid];
float top_p = top_ps[bid];
float sum_prob = 0.0f;
bool flag = false;
float max_val = 0.f;
int max_id = -1;
for (int i = 0; i < TopPBeamTopK; i++) {
if (i < k) {
topk_ids_now[i] = static_cast<int64_t>(beam_max[i].id);
topk_scores_now[i] = beam_max[i].v;
}
if (!flag) {
float val = static_cast<float>(beam_max[i].v);
sum_prob += val;
float random_ratio =
exponential_transform(GPU(rand_uniform)(states + bid), 1.0f);
float random_val = (val >= threshold_now ? val : 0.f) / random_ratio;
if (max_val < random_val) {
max_val = random_val;
max_id = i;
}
if (sum_prob >= top_p) {
flag = true;
count_iter_begin[bid] += 1;
if (max_id == -1) {
// don't sample low score token
out_id[bid] = static_cast<int64_t>(beam_max[0].id);
out_val[bid] = beam_max[0].v;
} else {
out_id[bid] = static_cast<int64_t>(beam_max[max_id].id);
out_val[bid] = beam_max[max_id].v;
}
}
}
if (flag && i >= k - 1) {
break;
}
}
}
}
template <typename T, int MaxLength, int TopPBeamTopK, int BlockSize>
__global__ void KeMatrixTopPBeamTopKFt(const T* src,
const T* threshold,
GPU(randState_t) * states,
T* top_ps,
int64_t* out_id, // topk id
T* out_val, // topk val
int64_t* topk_ids,
T* topk_scores,
int vocab_size,
int* count_iter,
int* count_iter_begin,
const int k,
const bool need_batch_random) {
const int tid = threadIdx.x;
const int wid = tid / WARP_SIZE;
const int lane = tid % WARP_SIZE;
const int bid = blockIdx.x;
const float threshold_now =
threshold ? static_cast<float>(threshold[bid]) : 0.f;
int top_num = TopPBeamTopK;
float top_p_num = static_cast<float>(top_ps[bid]);
int64_t* topk_ids_now = nullptr;
T* topk_scores_now = nullptr;
if (k > 0) {
topk_ids_now = topk_ids + bid * k;
topk_scores_now = topk_scores + bid * k;
}
__shared__ Pair<T> shared_max[BlockSize / WARP_SIZE];
__shared__ Pair<T> beam_max[TopPBeamTopK];
Pair<T> topk[MaxLength];
int beam = MaxLength;
Pair<T> max;
bool is_empty = false;
bool firststep = true;
__shared__ int count;
if (tid == 0) {
count = 0;
}
for (int j = 0; j < MaxLength; j++) {
topk[j].set(std::numeric_limits<T>::min(), -1);
}
while (top_num) {
ThreadGetTopK<T, MaxLength, BlockSize>(
topk,
&beam,
TopPBeamTopK,
src + static_cast<int64_t>(bid) * vocab_size,
&firststep,
&is_empty,
&max,
vocab_size,
tid);
BlockReduce<T, MaxLength, BlockSize>(
shared_max, topk, beam_max, &beam, &top_num, &count, tid, wid, lane);
}
if (tid == 0) {
count_iter_begin[bid] = count_iter[bid];
float rand_top_p = GPU(rand_uniform)(states + bid) * top_p_num;
top_ps[bid] = (T)rand_top_p;
float sum_prob = 0.0f;
bool flag = false;
for (int i = 0; i < TopPBeamTopK; i++) {
if (i < k) {
topk_ids_now[i] = static_cast<int64_t>(beam_max[i].id);
topk_scores_now[i] = beam_max[i].v;
}
if (!flag) {
float val = static_cast<float>(beam_max[i].v);
sum_prob += val;
#ifdef DEBUG_TOPP
printf("bi: %d, top_p: %f, rand_top_p: %f, sum_prob: %f\n",
bid,
top_p_num,
rand_top_p,
sum_prob);
#endif
if (sum_prob >= rand_top_p) {
flag = true;
count_iter_begin[bid] += 1;
if (val < threshold_now) {
// don't sample low score token
int start_id = i == 0 ? 0 : i - 1;
for (int j = start_id; j >= 0; j--) {
float val_now = static_cast<float>(beam_max[j].v);
if (val_now >= threshold_now || j == 0) {
out_id[bid] = static_cast<int64_t>(beam_max[j].id);
out_val[bid] = beam_max[j].v;
break;
}
}
} else {
out_id[bid] = static_cast<int64_t>(beam_max[i].id);
out_val[bid] = beam_max[i].v;
}
}
}
if (flag && i >= k - 1) {
break;
}
}
}
}
__global__ void SetCountIter(int* count_iter, int num) {
int tid = threadIdx.x;
int bid = blockIdx.x;
int idx = bid * blockDim.x + tid;
for (int64_t i = idx; i < num; i += static_cast<int64_t>(gridDim.x) *
static_cast<int64_t>(blockDim.x)) {
count_iter[i] = i;
}
}
template <typename T>
__global__ void FillIndex(T* indices, T num_rows, T num_cols) {
int col_id = threadIdx.x;
int row_id = blockIdx.x;
for (T j = row_id; j < num_rows; j += gridDim.x) {
for (T i = col_id; i < num_cols; i += blockDim.x) {
indices[j * num_cols + i] = i;
}
}
}
template <typename T, typename Context, int TopKMaxLength, int TopPBeamTopK>
void DispatchKeMatrixTopPBeamTopK(const Context& dev_ctx,
const T* src,
const T* threshold,
GPU(randState_t) * states,
T* top_ps,
int64_t* out_id, // topk id
T* out_val, // topk val
int64_t* topk_ids,
T* topk_scores,
int vocab_size,
int* count_iter,
int* count_iter_begin,
const int k,
const int bs,
const bool need_batch_random,
const std::string& mode) {
int BlockSize = GetBlockSize(vocab_size);
if (mode == "truncated") {
switch (BlockSize) {
FIXED_BLOCK_DIM(
KeMatrixTopPBeamTopKFt<T, TopKMaxLength, TopPBeamTopK, kBlockDim>
<<<bs, kBlockDim, 0, dev_ctx.stream()>>>(src,
threshold,
states,
top_ps,
out_id,
out_val,
topk_ids,
topk_scores,
vocab_size,
count_iter,
count_iter_begin,
k,
need_batch_random));
default:
PD_THROW(
"the input data shape has error in the topp_beam_topk kernel.");
}
} else {
switch (BlockSize) {
FIXED_BLOCK_DIM(
KeMatrixTopPBeamTopK<T, TopKMaxLength, TopPBeamTopK, kBlockDim>
<<<bs, kBlockDim, 0, dev_ctx.stream()>>>(src,
threshold,
states,
top_ps,
out_id,
out_val,
topk_ids,
topk_scores,
vocab_size,
count_iter,
count_iter_begin,
k,
need_batch_random));
default:
PD_THROW(
"the input data shape has error in the topp_beam_topk kernel.");
}
}
}
struct BlockPrefixCallbackOp {
// Running prefix
float running_total;
// Constructor
__device__ BlockPrefixCallbackOp(float running_total)
: running_total(running_total) {}
// Callback operator to be entered by the first warp of threads in the block.
// Thread-0 is responsible for returning a value for seeding the block-wide
// scan.
__device__ float operator()(float block_aggregate) {
float old_prefix = running_total;
running_total += block_aggregate;
return old_prefix;
}
};
template <typename T>
__device__ T max_func(const T a, const T b) {
return a > b ? a : b;
}
template <typename T>
struct MaxOp {
__device__ __forceinline__ T operator()(const T& a, const T& b) const {
return max_func(a, b);
}
};
template <typename T, int BLOCK_SIZE>
__global__ void topp_sampling(T* sorted_probs,
int64_t* sorted_id,
T* out_val,
int64_t* out_id,
const T* top_ps,
const T* threshold,
const int64_t* infer_seed,
GPU(randState_t) * states,
const int p_num,
const uint64_t seed,
const int vocab_size,
const bool need_batch_random,
int* count_iter,
int* count_iter_begin) {
__shared__ int stop_shared;
const int tid = threadIdx.x;
const int bid = blockIdx.x;
constexpr int NUM_WARPS = BLOCK_SIZE / WARP_SIZE;
const int lane_id = tid % WARP_SIZE;
const int warp_id = tid / WARP_SIZE;
const float p_t = static_cast<float>(top_ps[bid]);
const float threshold_now =
threshold ? static_cast<float>(threshold[bid]) : 0.f;
uint64_t seed_now;
GPU(randState_t) rand_state;
const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;
if (infer_seed) {
seed_now = static_cast<uint64_t>(infer_seed[bid]);
GPU(rand_init)(seed_now, tid, 0, &rand_state);
} else {
seed_now = seed;
GPU(rand_init)(seed_now, global_idx, 0, &rand_state);
}
if (tid == 0) {
stop_shared = 0;
}
if (count_iter_begin[bid] == count_iter[bid + 1]) {
// topk
return;
}
typedef cub::BlockScan<float, BLOCK_SIZE> BlockScan;
typedef cub::BlockReduce<Pair<T>, BLOCK_SIZE> BlockReduce;
__shared__ typename BlockScan::TempStorage temp_storage;
__shared__ typename BlockReduce::TempStorage temp_storage_reduce;
// Initialize running total
BlockPrefixCallbackOp prefix_op(0);
int64_t offset = static_cast<int64_t>(bid) * vocab_size;
#ifdef DEBUG_TOPP
if (tid == 0) {
printf(
"first_elem1_1: %f, first_elem1_2: %f, first_id1_1: %d, first_id1_2: "
"%d\n",
static_cast<float>(sorted_probs[offset]),
static_cast<float>(sorted_probs[offset + 1]),
static_cast<int>(sorted_id[offset]),
static_cast<int>(sorted_id[offset + 1]));
}
#endif
int end = ((vocab_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
int i_activate = 0;
float thread_offset = 0;
Pair<T> max_thread_pair(static_cast<T>(0.), -1);
for (int i = tid; i < end; i += BLOCK_SIZE) {
float thread_count =
(i < vocab_size) ? static_cast<float>(sorted_probs[offset + i]) : 0.f;
BlockScan(temp_storage)
.InclusiveSum(thread_count, thread_offset, prefix_op);
if (thread_offset < p_t ||
(thread_offset >= p_t && thread_offset - thread_count < p_t)) {
float random_ratio =
exponential_transform(GPU(rand_uniform)(&rand_state), 1.0f);
float tmp_val =
(thread_count >= threshold_now ? thread_count : 0.f) / random_ratio;
if (static_cast<float>(max_thread_pair.v) < tmp_val) {
max_thread_pair.set(static_cast<T>(tmp_val), i);
}
#ifdef DEBUG_TOPP
if (i < 10) {
printf(
"tid: %d, i: %d, random_ratio: %f, thread_count: %f, tmp_val: %f, "
"max_thread_pair.v: %f, max_thread_pair.id: %d\n",
tid,
i,
random_ratio,
thread_count,
tmp_val,
max_thread_pair.v,
static_cast<int>(max_thread_pair.id));
}
#endif
}
#ifdef DEBUG_TOPP
printf("tid: %d, thread_count: %f, thread_offset: %f\n",
tid,
thread_count,
thread_offset);
#endif
#ifdef PADDLE_WITH_HIP
uint64_t activate_mask = __ballot(p_t <= thread_offset);
#else
uint32_t activate_mask = __ballot_sync(FINAL_MASK, p_t <= thread_offset);
#endif
i_activate = i;
if (activate_mask != 0) {
if (lane_id == 0) {
atomicAdd(&stop_shared, 1);
}
}
__syncthreads();
if (stop_shared > 0) {
break;
}
}
__syncthreads();
Pair<T> max_pair = BlockReduce(temp_storage_reduce)
.Reduce(max_thread_pair, MaxOp<Pair<T>>());
if (tid == 0) {
if (max_pair.id == -1) {
max_pair.id = 0;
}
#ifdef DEBUG_TOPP
printf("max_id: %d, max_val: %f\n",
static_cast<int>(max_pair.id),
static_cast<float>(max_pair.v));
#endif
out_id[bid] = sorted_id[offset + max_pair.id];
out_val[bid] = sorted_probs[offset + max_pair.id];
}
}
template <typename T, int BLOCK_SIZE>
__global__ void topp_sampling_ft(T* sorted_probs,
int64_t* sorted_id,
T* out_val,
int64_t* out_id,
const T* top_ps,
const T* threshold,
GPU(randState_t) * states,
const int p_num,
const int vocab_size,
const bool need_batch_random,
int* count_iter,
int* count_iter_begin) {
__shared__ int stop_shared;
__shared__ float rand_p;
const int tid = threadIdx.x;
const int bid = blockIdx.x;
constexpr int NUM_WARPS = BLOCK_SIZE / WARP_SIZE;
const int lane_id = tid % WARP_SIZE;
const int warp_id = tid / WARP_SIZE;
const float p_t = static_cast<float>(top_ps[bid]);
const float threshold_now =
threshold ? static_cast<float>(threshold[bid]) : 0.f;
if (tid == 0) {
stop_shared = 0;
rand_p = p_t;
#ifdef DEBUG_TOPP
printf("bi: %d, p: %f\n", bid, rand_p);
#endif
}
if (count_iter_begin[bid] == count_iter[bid + 1]) {
// topk
return;
}
typedef cub::BlockScan<float, BLOCK_SIZE> BlockScan;
typedef cub::BlockReduce<int, BLOCK_SIZE> BlockReduce;
__shared__ typename BlockScan::TempStorage temp_storage;
__shared__ typename BlockReduce::TempStorage temp_storage_reduce;
#ifdef PADDLE_WITH_HIP
__shared__ uint64_t selected_shared[NUM_WARPS];
#else
__shared__ uint32_t selected_shared[NUM_WARPS];
#endif
int threshold_id = 0;
// Initialize running total
BlockPrefixCallbackOp prefix_op(0);
if (lane_id == 0) {
selected_shared[warp_id] = 0;
}
__syncthreads();
int64_t offset = static_cast<int64_t>(bid) * vocab_size;
#ifdef DEBUG_TOPP
if (tid == 0) {
printf(
"first_elem1_1: %f, first_elem1_2: %f, first_id1_1: %d, first_id1_2: "
"%d\n",
static_cast<float>(sorted_probs[offset]),
static_cast<float>(sorted_probs[offset + 1]),
static_cast<int>(sorted_id[offset]),
static_cast<int>(sorted_id[offset + 1]));
}
#endif
int end = ((vocab_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
int i_activate = 0;
float thread_offset = 0;
for (int i = tid; i < end; i += BLOCK_SIZE) {
float thread_count =
(i < vocab_size) ? static_cast<float>(sorted_probs[offset + i]) : 0.f;
if (i < vocab_size && thread_count >= threshold_now) {
threshold_id = i;
}
BlockScan(temp_storage)
.InclusiveSum(thread_count, thread_offset, prefix_op);
#ifdef PADDLE_WITH_HIP
uint64_t activate_mask = __ballot(rand_p <= thread_offset);
#else
uint32_t activate_mask = __ballot_sync(FINAL_MASK, rand_p <= thread_offset);
#endif
i_activate = i;
if (activate_mask != 0) {
if (lane_id == 0) {
atomicAdd(&stop_shared, 1);
selected_shared[warp_id] = activate_mask;
}
}
__syncthreads();
if (stop_shared > 0) {
break;
}
}
__syncthreads();
if (stop_shared == 0) {
if (tid == 0) {
out_id[bid] = sorted_id[offset];
out_val[bid] = sorted_probs[offset];
#ifdef DEBUG_TOPP
printf("stop_shared: %d, out_id: %d, out_val: %f\n",
static_cast<int>(stop_shared),
static_cast<int>(out_id[bid]),
static_cast<float>(out_val[bid]));
#endif
}
return;
}
#ifdef DEBUG_TOPP
if (tid == 0) {
printf(
"first_elem2_1: %f, first_elem2_2: %f, first_id2_1: %d, first_id2_2: "
"%d\n",
static_cast<float>(sorted_probs[offset]),
static_cast<float>(sorted_probs[offset + 1]),
static_cast<int>(sorted_id[offset]),
static_cast<int>(sorted_id[offset + 1]));
}
#endif
bool skip = (selected_shared[warp_id] > 0) ? false : true;
for (int i = 0; i < warp_id; i++) {
if (selected_shared[i] != 0) {
// If the previous has stopped, skip the current warp
skip = true;
}
}
if (!skip) {
#ifdef PADDLE_WITH_HIP
int active_lane_id =
WARP_SIZE - __popcll(selected_shared[warp_id]); // first not 0
#else
int active_lane_id =
WARP_SIZE - __popc(selected_shared[warp_id]); // first not 0
#endif
if (lane_id == active_lane_id) {
float val = static_cast<float>(sorted_probs[offset + i_activate]);
#ifdef DEBUG_TOPP
printf(
"active_lane_id: %d, i_activate: %d.\n", active_lane_id, i_activate);
for (int i = 0; i < active_lane_id; i++) {
printf("p %d, value: %f\n",
i,
static_cast<float>(sorted_probs[offset + i]));
}
#endif
if (val < threshold_now) {
// don't sample low score token
int max_id =
BlockReduce(temp_storage_reduce).Reduce(threshold_id, MaxOp<int>());
#ifdef PADDLE_WITH_HIP
hiprandStatePhilox4_32_10_t rng;
hiprand_init(bid * blockDim.x + tid, tid, 0, &rng);
int random_id = hiprand(&rng) % (max_id + 1);
#else
curandStatePhilox4_32_10_t rng;
curand_init(bid * blockDim.x + tid, tid, 0, &rng);
int random_id = curand(&rng) % (max_id + 1);
#endif
out_id[bid] = sorted_id[offset + random_id];
out_val[bid] = sorted_probs[offset + random_id];
} else {
out_id[bid] = sorted_id[offset + i_activate];
out_val[bid] = sorted_probs[offset + i_activate];
}
}
}
}
template <typename T, typename Context>
void DispatchTopPSampling(const Context& dev_ctx,
T* sorted_probs,
int64_t* sorted_id,
T* out_val,
int64_t* out_id,
const T* top_ps,
const T* threshold,
const int64_t* infer_seed,
GPU(randState_t) * states,
const int p_num,
const int vocab_size,
const int bs,
const uint64_t seed,
const bool need_batch_random,
int* count_iter,
int* count_iter_begin,
const std::string& mode) {
int BlockSize = GetBlockSize(vocab_size);
if (mode == "truncated") {
switch (BlockSize) {
FIXED_BLOCK_DIM(
topp_sampling_ft<T, kBlockDim>
<<<bs, kBlockDim, 0, dev_ctx.stream()>>>(sorted_probs,
sorted_id,
out_val,
out_id,
top_ps,
threshold,
states,
p_num,
vocab_size,
need_batch_random,
count_iter,
count_iter_begin));
default:
PD_THROW("the input data shape has error in the topp_sampling kernel.");
}
} else {
switch (BlockSize) {
FIXED_BLOCK_DIM(
topp_sampling<T, kBlockDim>
<<<bs, kBlockDim, 0, dev_ctx.stream()>>>(sorted_probs,
sorted_id,
out_val,
out_id,
top_ps,
threshold,
infer_seed,
states,
p_num,
seed,
vocab_size,
need_batch_random,
count_iter,
count_iter_begin));
default:
PD_THROW("the input data shape has error in the topp_sampling kernel.");
}
}
}
__global__ void setup_kernel(GPU(randState_t) * state,
int64_t* seed,
const int bs) {
int64_t idx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
for (int64_t i = idx; i < bs; i += static_cast<int64_t>(gridDim.x) *
static_cast<int64_t>(blockDim.x)) {
GPU(rand_init)(static_cast<uint64_t>(seed[i]), 0, 0, &state[i]);
}
}
__global__ void setup_kernel(GPU(randState_t) * state,
const uint64_t seed,
const uint64_t offset,
const int bs,
const bool need_batch_random) {
int64_t idx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
for (int64_t i = idx; i < bs; i += static_cast<int64_t>(gridDim.x) *
static_cast<int64_t>(blockDim.x)) {
if (need_batch_random) {
GPU(rand_init)(seed, i, offset, &state[i]);
} else {
GPU(rand_init)(seed, 0, offset, &state[i]);
}
}
}
template <typename T>
T* SafeGetTensorPtr(const DenseTensor& t) {
return const_cast<T*>(t.data<T>());
}
template <typename T>
T* SafeGetTensorPtr(const DenseTensor* t) {
return t ? SafeGetTensorPtr<T>(*t) : nullptr;
}
template <typename T>
T* SafeGetTensorPtr(const optional<DenseTensor>& t) {
return t ? SafeGetTensorPtr<T>(t.get()) : nullptr;
}
template <typename T, typename Context>
void TopPSamplingKernel(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& ps,
const optional<DenseTensor>& threshold,
const optional<DenseTensor>& topp_seed,
int64_t seed,
int k,
const std::string& mode,
DenseTensor* out,
DenseTensor* ids,
DenseTensor* topk_scores,
DenseTensor* topk_ids) {
typedef DataTypeTraits<T> traits_;
typedef typename traits_::DataType DataType_;
auto cu_stream = dev_ctx.stream();
const auto* input = &x;
// get the input dims
const auto& in_dims = input->dims();
int64_t p_num = ps.numel();
int64_t bs = in_dims[0];
// TODO(large-tensor): downstream functors may still use int
PADDLE_ENFORCE_LE_INT_MAX(p_num, "p_num");
PADDLE_ENFORCE_LE_INT_MAX(bs + 1, "bs + 1");
PADDLE_ENFORCE_LE_INT_MAX(in_dims[1], "vocab_size");
int64_t num_items64 = bs * in_dims[1];
PADDLE_ENFORCE_LE_INT_MAX(num_items64, "bs * vocab_size");
const int p_num_int = static_cast<int>(p_num);
const int vocab_size = static_cast<int>(in_dims[1]);
const int num_segments = static_cast<int>(bs);
const int num_segments_with_end = static_cast<int>(bs + 1);
const int num_items = static_cast<int>(num_items64);
T* out_ptr = dev_ctx.template Alloc<T>(out);
int64_t* ids_ptr = dev_ctx.template Alloc<int64_t>(ids);
T* topk_scores_data = nullptr;
int64_t* topk_ids_data = nullptr;
if (k > 0) {
topk_scores_data = dev_ctx.template Alloc<T>(topk_scores);
topk_ids_data = dev_ctx.template Alloc<int64_t>(topk_ids);
}
DenseTensor ps_now;
ps_now.Resize({bs, 1});
dev_ctx.template Alloc<T>(&ps_now);
Copy(dev_ctx, ps, dev_ctx.GetPlace(), false, &ps_now);
DenseTensor inds_input;
inds_input.Resize({bs, vocab_size});
dev_ctx.template Alloc<int64_t>(&inds_input);
DenseTensor sorted_out;
sorted_out.Resize({bs, vocab_size});
dev_ctx.template Alloc<T>(&sorted_out);
DenseTensor sorted_id;
sorted_id.Resize({bs, vocab_size});
dev_ctx.template Alloc<int64_t>(&sorted_id);
int BlockSize = GetBlockSize(vocab_size);
switch (BlockSize) {
FIXED_BLOCK_DIM(FillIndex<int64_t>
<<<num_segments, kBlockDim, 0, cu_stream>>>(
inds_input.data<int64_t>(), bs, vocab_size));
default:
PD_THROW("the input data shape has error in the FillIndex kernel.");
}
int64_t* infer_seed = SafeGetTensorPtr<int64_t>(topp_seed);
GPU(randState_t) * states{nullptr};
phi::Allocator::AllocationPtr rand_states_buf{nullptr};
rand_states_buf = phi::memory_utils::Alloc(
dev_ctx.GetPlace(),
bs * sizeof(GPU(randState_t)),
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx.stream())));
states = reinterpret_cast<GPU(randState_t)*>(rand_states_buf->ptr());
uint64_t seed_now = seed;
uint64_t offset = 0;
bool need_batch_random = false;
if (infer_seed) {
setup_kernel<<<1, 256, 0, cu_stream>>>(states, infer_seed, num_segments);
} else {
if (seed_now == -1) {
need_batch_random = true;
auto gen_cuda = dev_ctx.GetGenerator();
uint64_t increment = bs * BlockSize;
auto seed_offset = gen_cuda->IncrementOffset(increment);
seed_now = seed_offset.first;
offset = seed_offset.second;
setup_kernel<<<1, 256, 0, cu_stream>>>(
states, seed_now, offset, num_segments, need_batch_random);
} else {
setup_kernel<<<1, 256, 0, cu_stream>>>(
states, seed_now, offset, num_segments, need_batch_random);
}
}
DenseTensor count_iter;
count_iter.Resize({bs + 1});
dev_ctx.template Alloc<int>(&count_iter);
DenseTensor count_iter_begin;
count_iter_begin.Resize({bs});
dev_ctx.template Alloc<int>(&count_iter_begin);
SetCountIter<<<1, 256, 0, cu_stream>>>(count_iter.data<int>(),
num_segments_with_end);
T* threshold_data = SafeGetTensorPtr<T>(threshold);
constexpr int TopKMaxLength = 2;
constexpr int TopPBeamTopK = 20;
DispatchKeMatrixTopPBeamTopK<T, Context, TopKMaxLength, TopPBeamTopK>(
dev_ctx,
x.data<T>(),
threshold_data,
states,
ps_now.data<T>(),
ids_ptr,
out_ptr,
topk_ids_data,
topk_scores_data,
vocab_size,
count_iter.data<int>(),
count_iter_begin.data<int>(),
k,
num_segments,
need_batch_random,
mode);
size_t temp_storage_bytes = 0;
cub::TransformInputIterator<int, SegmentOffsetIter, int*>
segment_offsets_t_begin(count_iter_begin.data<int>(),
SegmentOffsetIter(vocab_size));
cub::TransformInputIterator<int, SegmentOffsetIter, int*>
segment_offsets_t_end(count_iter.data<int>(),
SegmentOffsetIter(vocab_size));
cub::DeviceSegmentedRadixSort::SortPairsDescending(
nullptr,
temp_storage_bytes,
reinterpret_cast<DataType_*>(const_cast<T*>(x.data<T>())),
reinterpret_cast<DataType_*>(const_cast<T*>(sorted_out.data<T>())),
inds_input.data<int64_t>(),
sorted_id.data<int64_t>(),
num_items,
num_segments,
segment_offsets_t_begin,
segment_offsets_t_end + 1,
0,
sizeof(T) * 8,
cu_stream);
temp_storage_bytes = div_up(temp_storage_bytes, 256) * 256;
int64_t temp_size = temp_storage_bytes;
DenseTensor temp_storage;
temp_storage.Resize({temp_size});
dev_ctx.template Alloc<uint8_t>(&temp_storage);
cub::DeviceSegmentedRadixSort::SortPairsDescending(
temp_storage.data<uint8_t>(),
temp_storage_bytes,
reinterpret_cast<DataType_*>(const_cast<T*>(x.data<T>())),
reinterpret_cast<DataType_*>(const_cast<T*>(sorted_out.data<T>())),
inds_input.data<int64_t>(),
sorted_id.data<int64_t>(),
num_items,
num_segments,
segment_offsets_t_begin,
segment_offsets_t_end + 1,
0,
sizeof(T) * 8,
cu_stream);
DispatchTopPSampling<T>(dev_ctx,
sorted_out.data<T>(),
sorted_id.data<int64_t>(),
out_ptr,
ids_ptr,
ps_now.data<T>(),
threshold_data,
infer_seed,
states,
p_num_int,
vocab_size,
num_segments,
seed_now + offset,
need_batch_random,
count_iter.data<int>(),
count_iter_begin.data<int>(),
mode);
}
} // namespace phi
#ifdef CUDA_BFLOAT16_AVAILABLE
PD_REGISTER_KERNEL(top_p_sampling,
GPU,
ALL_LAYOUT,
phi::TopPSamplingKernel,
float,
double,
int,
int64_t,
phi::float16,
phi::bfloat16) {}
#else
PD_REGISTER_KERNEL(top_p_sampling,
GPU,
ALL_LAYOUT,
phi::TopPSamplingKernel,
float,
double,
int,
int64_t,
phi::float16) {}
#endif