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

487 lines
17 KiB
C++

// Copyright (c) 2023 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.
#pragma once
#include <assert.h>
#include "paddle/phi/kernels/funcs/aligned_vector.h"
#include "paddle/phi/kernels/primitive/functor_primitives.h"
#define CUDART_INF __longlong_as_double(0x7ff0000000000000ULL)
#define CUDART_INF_F __int_as_float(0x7f800000)
namespace phi {
namespace funcs {
constexpr int kWarpSize = 32;
template <typename T>
__inline__ __device__ T Inf();
template <>
__inline__ __device__ float Inf<float>() {
return CUDART_INF_F;
}
template <>
__inline__ __device__ double Inf<double>() {
return CUDART_INF;
}
template <typename T,
template <typename>
class Functor,
int ThreadGroupWidth = kWarpSize>
__inline__ __device__ T WarpAllReduce(T val) {
for (int mask = ThreadGroupWidth / 2; mask > 0; mask /= 2) {
#if PADDLE_WITH_HIP
val = Functor<T>()(val, __shfl_xor(0xffffffff, val, mask));
#else
val = Functor<T>()(val, __shfl_xor_sync(0xffffffff, val, mask));
#endif
}
return val;
}
#if PADDLE_WITH_HIP
inline void GetNumBlocks(int64_t block_size,
int64_t max_blocks,
int64_t waves,
int* num_blocks) {
int dev;
PADDLE_ENFORCE_GPU_SUCCESS(hipGetDevice(&dev));
int sm_count;
PADDLE_ENFORCE_GPU_SUCCESS(hipDeviceGetAttribute(
&sm_count, hipDeviceAttributeMultiprocessorCount, dev));
int tpm;
PADDLE_ENFORCE_GPU_SUCCESS(hipDeviceGetAttribute(
&tpm, hipDeviceAttributeMaxThreadsPerMultiProcessor, dev));
*num_blocks = std::max<int>(
1, std::min<int64_t>(max_blocks, sm_count * tpm / block_size * waves));
}
#else
inline void GetNumBlocks(int64_t block_size,
int64_t max_blocks,
int64_t waves,
int* num_blocks) {
int dev;
PADDLE_ENFORCE_GPU_SUCCESS(cudaGetDevice(&dev));
int sm_count;
PADDLE_ENFORCE_GPU_SUCCESS(
cudaDeviceGetAttribute(&sm_count, cudaDevAttrMultiProcessorCount, dev));
int tpm;
PADDLE_ENFORCE_GPU_SUCCESS(cudaDeviceGetAttribute(
&tpm, cudaDevAttrMaxThreadsPerMultiProcessor, dev));
*num_blocks = std::max<int>(
1, std::min<int64_t>(max_blocks, sm_count * tpm / block_size * waves));
}
#endif
template <typename T,
typename SourceType,
typename Context,
int VecSize,
int ColsPerThread,
int RowsPerThread,
int ThreadGroupWidth,
bool NeedPadding>
__global__ void LogsumexpWarpImpl(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
static_assert(ColsPerThread % VecSize == 0, "");
static_assert(ThreadGroupWidth <= kWarpSize, "");
static_assert(kWarpSize % ThreadGroupWidth == 0, "");
constexpr int num_read = ColsPerThread / VecSize;
assert(num_col <= ColsPerThread * ThreadGroupWidth);
const int group_id = blockIdx.x * blockDim.y + threadIdx.y;
const int num_thread_group = gridDim.x * blockDim.y;
const int thread_id = threadIdx.x;
const int step = num_thread_group * RowsPerThread;
using LoadType = AlignedVector<SourceType, VecSize>;
using StoreType = AlignedVector<SourceType, RowsPerThread>;
LoadType load_vec;
StoreType store_vec;
T buffer[RowsPerThread][ColsPerThread];
for (int64_t cur_row = group_id * RowsPerThread; cur_row < num_row;
cur_row += step) {
T thread_max[RowsPerThread];
// Read data
#pragma unroll
for (int row_id = 0; row_id < RowsPerThread; row_id++) {
thread_max[row_id] = -Inf<T>();
T* row_buffer = buffer[row_id];
#pragma unroll
for (int read_id = 0; read_id < num_read; read_id++) {
const int offset = read_id * VecSize;
const int cur_col = (read_id * ThreadGroupWidth + thread_id) * VecSize;
if (!NeedPadding || cur_col < num_col) {
int64_t load_offset = ((cur_row + row_id) * num_col + cur_col);
Load<SourceType, VecSize>(in + load_offset, &load_vec);
#pragma unroll
for (int i = 0; i < VecSize; i++) {
row_buffer[offset + i] = static_cast<T>(load_vec[i]);
thread_max[row_id] =
max(thread_max[row_id], row_buffer[offset + i]);
}
} else {
#pragma unroll
for (int i = 0; i < VecSize; i++) {
row_buffer[offset + i] = -Inf<T>();
}
}
}
}
T warp_max[RowsPerThread];
// Get warp max
#pragma unroll
for (int row_id = 0; row_id < RowsPerThread; row_id++) {
warp_max[row_id] = WarpAllReduce<T, kps::MaxFunctor, ThreadGroupWidth>(
thread_max[row_id]);
}
T thread_sum[RowsPerThread];
// Calculate
#pragma unroll
for (int row_id = 0; row_id < RowsPerThread; row_id++) {
thread_sum[row_id] = 0;
T* row_buffer = buffer[row_id];
#pragma unroll
for (int i = 0; i < ColsPerThread; i++) {
thread_sum[row_id] += exp(row_buffer[i] - warp_max[row_id]);
}
}
// Get warp sum and write
#pragma unroll
for (int row_id = 0; row_id < RowsPerThread; row_id++) {
T res = log(WarpAllReduce<T, kps::AddFunctor, ThreadGroupWidth>(
thread_sum[row_id]));
store_vec[row_id] = static_cast<SourceType>(res + warp_max[row_id]);
}
if (thread_id == 0 && cur_row < num_row) {
Store<SourceType, RowsPerThread>(store_vec, out + cur_row);
}
}
}
template <typename T,
typename SourceType,
typename Context,
int VecSize,
int ColsPerThread,
int RowsPerThread,
int ThreadGroupWidth,
bool NeedPadding>
#if PADDLE_WITH_HIP
inline hipError_t LaunchLogsumexpWarp(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#else
inline cudaError_t LaunchLogsumexpWarp(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#endif
constexpr int block_size = 128;
constexpr int waves = 32;
static_assert(block_size % ThreadGroupWidth == 0, "");
constexpr int thread_groups_per_block = block_size / ThreadGroupWidth;
dim3 block_dim(ThreadGroupWidth, thread_groups_per_block);
const int64_t num_blocks =
(num_row / RowsPerThread + thread_groups_per_block - 1) /
thread_groups_per_block;
int grid_dim_x;
{ GetNumBlocks(block_size, num_blocks, waves, &grid_dim_x); }
LogsumexpWarpImpl<T,
SourceType,
Context,
VecSize,
ColsPerThread,
RowsPerThread,
ThreadGroupWidth,
NeedPadding>
<<<grid_dim_x, block_dim, 0, dev_ctx.stream()>>>(
dev_ctx, num_row, num_col, in, out);
#if PADDLE_WITH_HIP
return hipPeekAtLastError();
#else
return cudaPeekAtLastError();
#endif
}
template <typename T,
typename SourceType,
typename Context,
int VecSize,
int ColsPerThread,
int RowsPerThread,
int ThreadGroupWidth>
#if PADDLE_WITH_HIP
inline hipError_t DispatchLogsumexpWarpWithPadding(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#else
inline cudaError_t DispatchLogsumexpWarpWithPadding(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#endif
if (num_col == ColsPerThread * ThreadGroupWidth) {
return LaunchLogsumexpWarp<T,
SourceType,
Context,
VecSize,
ColsPerThread,
RowsPerThread,
ThreadGroupWidth,
false>(dev_ctx, num_row, num_col, in, out);
} else {
return LaunchLogsumexpWarp<T,
SourceType,
Context,
VecSize,
ColsPerThread,
RowsPerThread,
ThreadGroupWidth,
true>(dev_ctx, num_row, num_col, in, out);
}
}
template <typename T, typename SourceType, typename Context, int VecSize>
#if PADDLE_WITH_HIP
typename std::enable_if<VecSize == 1, hipError_t>::type
DispatchLogsumexpWarpCols(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#else
typename std::enable_if<VecSize == 1, cudaError_t>::type
DispatchLogsumexpWarpCols(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#endif
if (num_col <= 0) {
#if PADDLE_WITH_HIP
return hipErrorInvalidValue;
#else
return cudaErrorInvalidValue;
#endif
}
#define HANDLE_THREAD_GROUP(thread_group_width) \
if (num_col <= (thread_group_width)*VecSize) { \
if (num_row % 2 == 0) { \
return DispatchLogsumexpWarpWithPadding<T, \
SourceType, \
Context, \
VecSize, \
VecSize, \
2, \
thread_group_width>( \
dev_ctx, num_row, num_col, in, out); \
} else { \
return DispatchLogsumexpWarpWithPadding<T, \
SourceType, \
Context, \
VecSize, \
VecSize, \
1, \
thread_group_width>( \
dev_ctx, num_row, num_col, in, out); \
} \
}
HANDLE_THREAD_GROUP(1)
HANDLE_THREAD_GROUP(2)
HANDLE_THREAD_GROUP(4)
HANDLE_THREAD_GROUP(8)
HANDLE_THREAD_GROUP(16)
HANDLE_THREAD_GROUP(32)
#undef HANDLE_ROWS
// if num_col > 32
#define HANDLE_COL(col) \
if (num_col <= (col)*kWarpSize) { \
return DispatchLogsumexpWarpWithPadding<T, \
SourceType, \
Context, \
VecSize, \
col, \
1, \
kWarpSize>( \
dev_ctx, num_row, num_col, in, out); \
}
HANDLE_COL(2)
HANDLE_COL(3)
HANDLE_COL(4)
HANDLE_COL(5)
HANDLE_COL(6)
HANDLE_COL(7)
HANDLE_COL(8)
HANDLE_COL(9)
HANDLE_COL(10)
HANDLE_COL(11)
HANDLE_COL(12)
HANDLE_COL(13)
HANDLE_COL(14)
HANDLE_COL(15)
HANDLE_COL(16)
HANDLE_COL(17)
HANDLE_COL(18)
HANDLE_COL(19)
HANDLE_COL(20)
HANDLE_COL(21)
HANDLE_COL(22)
HANDLE_COL(23)
HANDLE_COL(24)
HANDLE_COL(25)
HANDLE_COL(26)
HANDLE_COL(27)
HANDLE_COL(28)
HANDLE_COL(29)
HANDLE_COL(30)
HANDLE_COL(31)
HANDLE_COL(32)
#undef HANDLE_COL
#if PADDLE_WITH_HIP
return hipErrorInvalidValue;
#else
return cudaErrorInvalidValue;
#endif
}
template <typename T, typename SourceType, typename Context, int VecSize>
#if PADDLE_WITH_HIP
typename std::enable_if<VecSize == 2, hipError_t>::type
DispatchLogsumexpWarpCols(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#else
typename std::enable_if<VecSize == 2, cudaError_t>::type
DispatchLogsumexpWarpCols(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#endif
if (num_col <= 0) {
#if PADDLE_WITH_HIP
return hipErrorInvalidValue;
#else
return cudaErrorInvalidValue;
#endif
}
#define HANDLE_THREAD_GROUP(thread_group_width) \
if (num_col <= (thread_group_width)*VecSize) { \
if (num_row % 2 == 0) { \
return DispatchLogsumexpWarpWithPadding<T, \
SourceType, \
Context, \
VecSize, \
VecSize, \
2, \
thread_group_width>( \
dev_ctx, num_row, num_col, in, out); \
} else { \
return DispatchLogsumexpWarpWithPadding<T, \
SourceType, \
Context, \
VecSize, \
VecSize, \
1, \
thread_group_width>( \
dev_ctx, num_row, num_col, in, out); \
} \
}
HANDLE_THREAD_GROUP(1)
HANDLE_THREAD_GROUP(2)
HANDLE_THREAD_GROUP(4)
HANDLE_THREAD_GROUP(8)
HANDLE_THREAD_GROUP(16)
HANDLE_THREAD_GROUP(32)
#undef HANDLE_THREAD_GROUP
// if num_col > 32
#define HANDLE_COL(col) \
if (num_col <= (col)*kWarpSize) { \
return DispatchLogsumexpWarpWithPadding<T, \
SourceType, \
Context, \
VecSize, \
col, \
1, \
kWarpSize>( \
dev_ctx, num_row, num_col, in, out); \
}
HANDLE_COL(4)
HANDLE_COL(6)
HANDLE_COL(8)
HANDLE_COL(10)
HANDLE_COL(12)
HANDLE_COL(14)
HANDLE_COL(16)
HANDLE_COL(18)
HANDLE_COL(20)
HANDLE_COL(22)
HANDLE_COL(24)
HANDLE_COL(26)
HANDLE_COL(28)
HANDLE_COL(30)
HANDLE_COL(32)
#undef HANDLE_COL
#if PADDLE_WITH_HIP
return hipErrorInvalidValue;
#else
return cudaErrorInvalidValue;
#endif
}
template <typename T, typename SourceType, typename Context>
#if PADDLE_WITH_HIP
inline hipError_t DispatchLogsumexpWarp(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#else
inline cudaError_t DispatchLogsumexpWarp(const Context& dev_ctx,
const int64_t num_row,
const int64_t num_col,
const SourceType* in,
SourceType* out) {
#endif
// dispatch logsumexp warp with vecsize
if (num_col % 2 == 0) {
return DispatchLogsumexpWarpCols<T, SourceType, Context, 2>(
dev_ctx, num_row, num_col, in, out);
} else {
return DispatchLogsumexpWarpCols<T, SourceType, Context, 1>(
dev_ctx, num_row, num_col, in, out);
}
}
} // namespace funcs
} // namespace phi