// 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 #include #include #else #include #include #endif #include "paddle/phi/kernels/funcs/cub.h" #if defined(__CUDACC__) && CUDA_VERSION >= 11060 #define CUDA_BFLOAT16_AVAILABLE #include #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 struct DataTypeTraits { using DataType = T; }; template <> struct DataTypeTraits { using DataType = half; }; #ifdef CUDA_BFLOAT16_AVAILABLE template <> struct DataTypeTraits { 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(idx) * num_cols_, "idx * num_cols_"); #endif return static_cast(static_cast(idx) * num_cols_); } int num_cols_; }; template 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& in) { v = in.v; id = in.id; } __device__ __forceinline__ bool operator<(const T value) const { return (static_cast(v) < static_cast(value)); } __device__ __forceinline__ bool operator>(const T value) const { return (static_cast(v) > static_cast(value)); } __device__ __forceinline__ bool operator<(const Pair& in) const { return (static_cast(v) < static_cast(in.v)) || ((static_cast(v) == static_cast(in.v)) && (id > in.id)); } __device__ __forceinline__ bool operator>(const Pair& in) const { return (static_cast(v) > static_cast(in.v)) || ((static_cast(v) == static_cast(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 __device__ __forceinline__ void AddTo(Pair topk[], const Pair& 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 __device__ __forceinline__ void GetTopK( Pair topk[], const T* src, int idx, int dim, int beam_size) { while (idx < dim) { if (topk[beam_size - 1] < src[idx]) { Pair tmp(src[idx], idx); AddTo(topk, tmp, beam_size); } idx += BlockSize; } } template __device__ __forceinline__ void GetTopK(Pair topk[], const T* src, int idx, int dim, const Pair& max, int beam_size) { while (idx < dim) { if (topk[beam_size - 1] < src[idx]) { Pair tmp(src[idx], idx); if (tmp < max) { AddTo(topk, tmp, beam_size); } } idx += BlockSize; } } template __device__ __forceinline__ void ThreadGetTopK(Pair topk[], int* beam, int beam_size, const T* src, bool* firstStep, bool* is_empty, Pair* max, int dim, const int tid) { if (*beam > 0) { int length = (*beam) < beam_size ? *beam : beam_size; if (*firstStep) { *firstStep = false; GetTopK(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::min(), -1); } } if (!(*is_empty)) { GetTopK( topk + MaxLength - *beam, src, tid, dim, *max, length); } } *max = topk[MaxLength - 1]; if ((*max).id == -1) *is_empty = true; *beam = 0; } } template __forceinline__ __device__ Pair WarpReduce(Pair 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(input.v) < static_cast(tmp_val)) { input.v = tmp_val; input.id = tmp_id; } } return input; } template __device__ __forceinline__ void BlockReduce(Pair shared_max[], Pair topk[], Pair beam_max[], int* beam, int* k, int* count, const int tid, const int wid, const int lane) { while (true) { __syncthreads(); Pair 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(std::numeric_limits::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 __device__ inline T exponential_transform(T val, T lambda) { #if defined(__NVCC__) || defined(__HIPCC__) T log = -std::numeric_limits::epsilon() / 2; if (val < static_cast(1.) - std::numeric_limits::epsilon() / 2) { if (std::is_same::value) { log = logf(val); } else { log = __logf(val); } } return static_cast(-1.0) / lambda * log; #else return static_cast(-1.0) / lambda * std::log(static_cast(1.0) - val); #endif } template __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(threshold[bid]) : 0.f; int top_num = TopPBeamTopK; float top_p_num = static_cast(top_ps[bid]); const int64_t offset = static_cast(bid) * vocab_size; int64_t* topk_ids_now = nullptr; T* topk_scores_now = nullptr; if (k > 0) { topk_ids_now = topk_ids + static_cast(bid) * k; topk_scores_now = topk_scores + static_cast(bid) * k; } __shared__ Pair shared_max[BlockSize / WARP_SIZE]; __shared__ Pair beam_max[TopPBeamTopK]; Pair topk[MaxLength]; int beam = MaxLength; Pair 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::min(), -1); } while (top_num) { ThreadGetTopK(topk, &beam, TopPBeamTopK, src + offset, &firststep, &is_empty, &max, vocab_size, tid); BlockReduce( 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(beam_max[i].id); topk_scores_now[i] = beam_max[i].v; } if (!flag) { float val = static_cast(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(beam_max[0].id); out_val[bid] = beam_max[0].v; } else { out_id[bid] = static_cast(beam_max[max_id].id); out_val[bid] = beam_max[max_id].v; } } } if (flag && i >= k - 1) { break; } } } } template __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(threshold[bid]) : 0.f; int top_num = TopPBeamTopK; float top_p_num = static_cast(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 shared_max[BlockSize / WARP_SIZE]; __shared__ Pair beam_max[TopPBeamTopK]; Pair topk[MaxLength]; int beam = MaxLength; Pair 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::min(), -1); } while (top_num) { ThreadGetTopK( topk, &beam, TopPBeamTopK, src + static_cast(bid) * vocab_size, &firststep, &is_empty, &max, vocab_size, tid); BlockReduce( 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(beam_max[i].id); topk_scores_now[i] = beam_max[i].v; } if (!flag) { float val = static_cast(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(beam_max[j].v); if (val_now >= threshold_now || j == 0) { out_id[bid] = static_cast(beam_max[j].id); out_val[bid] = beam_max[j].v; break; } } } else { out_id[bid] = static_cast(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(gridDim.x) * static_cast(blockDim.x)) { count_iter[i] = i; } } template __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 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 <<>>(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 <<>>(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 __device__ T max_func(const T a, const T b) { return a > b ? a : b; } template struct MaxOp { __device__ __forceinline__ T operator()(const T& a, const T& b) const { return max_func(a, b); } }; template __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(top_ps[bid]); const float threshold_now = threshold ? static_cast(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(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 BlockScan; typedef cub::BlockReduce, 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(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(sorted_probs[offset]), static_cast(sorted_probs[offset + 1]), static_cast(sorted_id[offset]), static_cast(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 max_thread_pair(static_cast(0.), -1); for (int i = tid; i < end; i += BLOCK_SIZE) { float thread_count = (i < vocab_size) ? static_cast(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(max_thread_pair.v) < tmp_val) { max_thread_pair.set(static_cast(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(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 max_pair = BlockReduce(temp_storage_reduce) .Reduce(max_thread_pair, MaxOp>()); 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(max_pair.id), static_cast(max_pair.v)); #endif out_id[bid] = sorted_id[offset + max_pair.id]; out_val[bid] = sorted_probs[offset + max_pair.id]; } } template __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(top_ps[bid]); const float threshold_now = threshold ? static_cast(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 BlockScan; typedef cub::BlockReduce 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(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(sorted_probs[offset]), static_cast(sorted_probs[offset + 1]), static_cast(sorted_id[offset]), static_cast(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(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(stop_shared), static_cast(out_id[bid]), static_cast(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(sorted_probs[offset]), static_cast(sorted_probs[offset + 1]), static_cast(sorted_id[offset]), static_cast(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(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(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()); #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 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 <<>>(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 <<>>(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(blockIdx.x) * static_cast(blockDim.x) + static_cast(threadIdx.x); for (int64_t i = idx; i < bs; i += static_cast(gridDim.x) * static_cast(blockDim.x)) { GPU(rand_init)(static_cast(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(blockIdx.x) * static_cast(blockDim.x) + static_cast(threadIdx.x); for (int64_t i = idx; i < bs; i += static_cast(gridDim.x) * static_cast(blockDim.x)) { if (need_batch_random) { GPU(rand_init)(seed, i, offset, &state[i]); } else { GPU(rand_init)(seed, 0, offset, &state[i]); } } } template T* SafeGetTensorPtr(const DenseTensor& t) { return const_cast(t.data()); } template T* SafeGetTensorPtr(const DenseTensor* t) { return t ? SafeGetTensorPtr(*t) : nullptr; } template T* SafeGetTensorPtr(const optional& t) { return t ? SafeGetTensorPtr(t.get()) : nullptr; } template void TopPSamplingKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& ps, const optional& threshold, const optional& topp_seed, int64_t seed, int k, const std::string& mode, DenseTensor* out, DenseTensor* ids, DenseTensor* topk_scores, DenseTensor* topk_ids) { typedef DataTypeTraits 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(p_num); const int vocab_size = static_cast(in_dims[1]); const int num_segments = static_cast(bs); const int num_segments_with_end = static_cast(bs + 1); const int num_items = static_cast(num_items64); T* out_ptr = dev_ctx.template Alloc(out); int64_t* ids_ptr = dev_ctx.template Alloc(ids); T* topk_scores_data = nullptr; int64_t* topk_ids_data = nullptr; if (k > 0) { topk_scores_data = dev_ctx.template Alloc(topk_scores); topk_ids_data = dev_ctx.template Alloc(topk_ids); } DenseTensor ps_now; ps_now.Resize({bs, 1}); dev_ctx.template Alloc(&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(&inds_input); DenseTensor sorted_out; sorted_out.Resize({bs, vocab_size}); dev_ctx.template Alloc(&sorted_out); DenseTensor sorted_id; sorted_id.Resize({bs, vocab_size}); dev_ctx.template Alloc(&sorted_id); int BlockSize = GetBlockSize(vocab_size); switch (BlockSize) { FIXED_BLOCK_DIM(FillIndex <<>>( inds_input.data(), bs, vocab_size)); default: PD_THROW("the input data shape has error in the FillIndex kernel."); } int64_t* infer_seed = SafeGetTensorPtr(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(dev_ctx.stream()))); states = reinterpret_cast(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(&count_iter); DenseTensor count_iter_begin; count_iter_begin.Resize({bs}); dev_ctx.template Alloc(&count_iter_begin); SetCountIter<<<1, 256, 0, cu_stream>>>(count_iter.data(), num_segments_with_end); T* threshold_data = SafeGetTensorPtr(threshold); constexpr int TopKMaxLength = 2; constexpr int TopPBeamTopK = 20; DispatchKeMatrixTopPBeamTopK( dev_ctx, x.data(), threshold_data, states, ps_now.data(), ids_ptr, out_ptr, topk_ids_data, topk_scores_data, vocab_size, count_iter.data(), count_iter_begin.data(), k, num_segments, need_batch_random, mode); size_t temp_storage_bytes = 0; cub::TransformInputIterator segment_offsets_t_begin(count_iter_begin.data(), SegmentOffsetIter(vocab_size)); cub::TransformInputIterator segment_offsets_t_end(count_iter.data(), SegmentOffsetIter(vocab_size)); cub::DeviceSegmentedRadixSort::SortPairsDescending( nullptr, temp_storage_bytes, reinterpret_cast(const_cast(x.data())), reinterpret_cast(const_cast(sorted_out.data())), inds_input.data(), sorted_id.data(), 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(&temp_storage); cub::DeviceSegmentedRadixSort::SortPairsDescending( temp_storage.data(), temp_storage_bytes, reinterpret_cast(const_cast(x.data())), reinterpret_cast(const_cast(sorted_out.data())), inds_input.data(), sorted_id.data(), num_items, num_segments, segment_offsets_t_begin, segment_offsets_t_end + 1, 0, sizeof(T) * 8, cu_stream); DispatchTopPSampling(dev_ctx, sorted_out.data(), sorted_id.data(), out_ptr, ids_ptr, ps_now.data(), threshold_data, infer_seed, states, p_num_int, vocab_size, num_segments, seed_now + offset, need_batch_random, count_iter.data(), count_iter_begin.data(), 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