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

749 lines
27 KiB
Plaintext

// 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.
#ifdef PADDLE_WITH_CUDA
#include <cuda.h>
#include <cuda_runtime.h>
#endif
#ifdef PADDLE_WITH_HIP
#include <hip/hip_runtime.h>
#endif
#include "paddle/phi/kernels/funcs/blas/blas.h"
#include "paddle/phi/kernels/funcs/cub.h"
#include "paddle/phi/kernels/funcs/math_cuda_utils.h"
#include "paddle/phi/kernels/funcs/multihead_matmul_functor.h"
namespace phi {
namespace funcs {
template <typename T>
struct CUDATypeTraits;
template <>
struct CUDATypeTraits<half> {
typedef phi::float16 TYPE;
};
template <>
struct CUDATypeTraits<float> {
typedef float TYPE;
};
using funcs::operator+;
template <typename T>
__global__ void SoftmaxKernelWithEltadd(T *qk_buf_,
const T *bias_qk_,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
assert(blockDim.x % WARP_SIZE == 0);
float tmp = threadIdx.x < seq_len
? static_cast<float>(qk_buf_[threadIdx.x + qk_offset] +
bias_qk_[threadIdx.x + qk_offset])
: -1e20f;
float max_val = funcs::BlockReduceMax<float>(tmp, mask);
float qk_tmp = threadIdx.x < seq_len ? __expf(tmp - max_val) : 0.0f;
float sum_val = funcs::BlockReduceSum<float>(qk_tmp, mask);
if (threadIdx.x < seq_len)
qk_buf_[threadIdx.x + qk_offset] = (T)(qk_tmp / sum_val);
}
template <>
__global__ void SoftmaxKernelWithEltadd<half>(half *qk_buf_,
const half *bias_qk_,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
#if defined(PADDLE_WITH_CUDA)
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
assert(blockDim.x % WARP_SIZE == 0);
float tmp = threadIdx.x < seq_len
? static_cast<float>(qk_buf_[threadIdx.x + qk_offset] +
bias_qk_[threadIdx.x + qk_offset])
: -1e20f;
float max_val = funcs::BlockReduceMax<float>(tmp, mask);
float qk_tmp = threadIdx.x < seq_len ? __expf(tmp - max_val) : 0.0f;
float sum_val = funcs::BlockReduceSum<float>(qk_tmp, mask);
if (threadIdx.x < seq_len)
qk_buf_[threadIdx.x + qk_offset] = (half)(qk_tmp / sum_val);
#endif
}
template <typename T>
__global__ void SoftmaxKernelWithEltadd2(T *qk_buf_,
const T *bias_qk_,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
int idx = threadIdx.x;
assert(blockDim.x % WARP_SIZE == 0);
float2 tmp = idx < seq_len ? funcs::ToFloat2<T>(qk_buf_[idx + qk_offset] +
bias_qk_[idx + qk_offset])
: make_float2(-1e20f, -1e20f);
float max_val = funcs::BlockReduceMax<float>(max(tmp.x, tmp.y), mask);
float2 qk_tmp = idx < seq_len ? make_float2(__expf(tmp.x - max_val),
__expf(tmp.y - max_val))
: make_float2(0.f, 0.f);
float sum_val =
funcs::BlockReduceSum<float>(qk_tmp.x + qk_tmp.y, mask) + 1e-6f;
if (idx < seq_len) {
qk_buf_[idx + qk_offset] =
funcs::FloatsToPair<T>(qk_tmp.x / sum_val, qk_tmp.y / sum_val);
}
}
template <>
__global__ void SoftmaxKernelWithEltadd2<half2>(half2 *qk_buf_,
const half2 *bias_qk_,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
// operator "+" of half only supported after cuda version 10.0
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#if defined(PADDLE_WITH_CUDA)
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
int idx = threadIdx.x;
assert(blockDim.x % WARP_SIZE == 0);
float2 tmp = idx < seq_len ? funcs::ToFloat2<half2>(qk_buf_[idx + qk_offset] +
bias_qk_[idx + qk_offset])
: make_float2(-1e20f, -1e20f);
float max_val = funcs::BlockReduceMax<float>(max(tmp.x, tmp.y), mask);
float2 qk_tmp = idx < seq_len ? make_float2(__expf(tmp.x - max_val),
__expf(tmp.y - max_val))
: make_float2(0.f, 0.f);
float sum_val =
funcs::BlockReduceSum<float>(qk_tmp.x + qk_tmp.y, mask) + 1e-6f;
if (idx < seq_len) {
qk_buf_[idx + qk_offset] =
funcs::FloatsToPair<half2>(qk_tmp.x / sum_val, qk_tmp.y / sum_val);
}
#endif
}
template <typename T>
__global__ void SoftmaxKernelWithEltaddForLarge(T *qk_buf,
const T *bias_qk,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
assert(blockDim.x % WARP_SIZE == 0);
T stride_max = -1e20f;
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
stride_max = qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset] >
stride_max
? qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset]
: stride_max;
}
T max_val = funcs::BlockReduceMax<T>(stride_max, mask);
T stride_sum = 0.f;
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
stride_sum += __expf(qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset] - max_val);
}
T sum_val = funcs::BlockReduceSum<T>(stride_sum, mask);
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
qk_buf[threadIdx.x + i + qk_offset] =
(T)(__expf(qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset] - max_val) /
sum_val);
}
}
template <>
__global__ void SoftmaxKernelWithEltaddForLarge(half *qk_buf,
const half *bias_qk,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
#if defined(PADDLE_WITH_CUDA)
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
assert(blockDim.x % WARP_SIZE == 0);
float stride_max = -1e20f;
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float tmp = static_cast<float>(qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset]);
stride_max = tmp > stride_max ? tmp : stride_max;
}
float max_val = funcs::BlockReduceMax<float>(stride_max, mask);
float stride_sum = 0.f;
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float tmp = static_cast<float>(qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset]);
stride_sum += __expf(tmp - max_val);
}
float sum_val = funcs::BlockReduceSum<float>(stride_sum, mask);
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float tmp =
__expf(static_cast<float>(qk_buf[threadIdx.x + i + qk_offset] +
bias_qk[threadIdx.x + i + qk_offset]) -
max_val);
qk_buf[threadIdx.x + i + qk_offset] = (half)(tmp / sum_val);
}
#endif
}
template <typename T>
__global__ void SoftmaxKernelWithEltaddForLarge2(
T *qk_buf_,
const T *bias_qk_,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
assert(blockDim.x % WARP_SIZE == 0);
float2 stride_max = make_float2(-1e20f, -1e20f);
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float2 cur = funcs::ToFloat2<T>(qk_buf_[threadIdx.x + i + qk_offset] +
bias_qk_[threadIdx.x + i + qk_offset]);
stride_max.x = max(stride_max.x, cur.x);
stride_max.y = max(stride_max.y, cur.y);
}
float max_val =
funcs::BlockReduceMax<float>(max(stride_max.x, stride_max.y), mask);
float2 stride_sum = make_float2(0.f, 0.f);
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float2 cur = funcs::ToFloat2<T>(qk_buf_[threadIdx.x + i + qk_offset] +
bias_qk_[threadIdx.x + i + qk_offset]);
stride_sum.x += __expf(cur.x - max_val);
stride_sum.y += __expf(cur.y - max_val);
}
float sum_val =
funcs::BlockReduceSum<float>(stride_sum.x + stride_sum.y, mask) + 1e-6f;
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float2 cur = funcs::ToFloat2<T>(qk_buf_[threadIdx.x + i + qk_offset] +
bias_qk_[threadIdx.x + i + qk_offset]);
qk_buf_[threadIdx.x + i + qk_offset] = funcs::FloatsToPair<T>(
__expf(cur.x - max_val) / sum_val, __expf(cur.y - max_val) / sum_val);
}
}
template <>
__global__ void SoftmaxKernelWithEltaddForLarge2(
half2 *qk_buf_,
const half2 *bias_qk_,
const int batch_size,
const int head_num,
const int seq_len,
const funcs::warp_mask_t mask) {
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#if defined(PADDLE_WITH_CUDA)
int64_t qk_offset = static_cast<int64_t>(blockIdx.x) * seq_len;
assert(blockDim.x % WARP_SIZE == 0);
float2 stride_max = make_float2(-1e20f, -1e20f);
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float2 cur = funcs::ToFloat2<half2>(qk_buf_[threadIdx.x + i + qk_offset] +
bias_qk_[threadIdx.x + i + qk_offset]);
stride_max.x = max(stride_max.x, cur.x);
stride_max.y = max(stride_max.y, cur.y);
}
float max_val =
funcs::BlockReduceMax<float>(max(stride_max.x, stride_max.y), mask);
float2 stride_sum = make_float2(0.f, 0.f);
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float2 cur = funcs::ToFloat2<half2>(qk_buf_[threadIdx.x + i + qk_offset] +
bias_qk_[threadIdx.x + i + qk_offset]);
stride_sum.x += __expf(cur.x - max_val);
stride_sum.y += __expf(cur.y - max_val);
}
float sum_val =
funcs::BlockReduceSum<float>(stride_sum.x + stride_sum.y, mask) + 1e-6f;
for (int i = 0; threadIdx.x + i < seq_len; i += blockDim.x) {
float2 cur = funcs::ToFloat2<half2>(qk_buf_[threadIdx.x + i + qk_offset] +
bias_qk_[threadIdx.x + i + qk_offset]);
qk_buf_[threadIdx.x + i + qk_offset] = funcs::FloatsToPair<half2>(
__expf(cur.x - max_val) / sum_val, __expf(cur.y - max_val) / sum_val);
}
#endif
}
template <typename T>
inline __device__ T ldg(const T *val) {
return __ldg(val);
}
template <typename T>
inline __device__ T hexp2(T a) {
return h2exp(a);
}
template <typename T_IN, typename T_OUT>
inline __device__ T_OUT type2type2(T_IN a);
template <>
inline __device__ half2 type2type2(half a) {
return __half2half2(a);
}
template <typename T>
inline __device__ T float2type2(float a);
template <>
inline __device__ half2 float2type2(float a) {
return __float2half2_rn(a);
}
template <typename T>
inline __device__ T hmul2(T a, T b) {
return __hmul2(a, b);
}
template <typename T>
inline __device__ T hsub2(T a, T b) {
return __hsub2(a, b);
}
template <typename T>
inline __device__ T hadd2(T a, T b) {
return __hadd2(a, b);
}
template <typename T, int ITEMS_PER_THREAD, int NUM>
__global__ void softmax_kernel_with_mask(T *qk_buf_,
const T *attr_mask,
const int batch_size,
const int head_num,
const int seq_len) {
using T2 = half2;
T2 *qk_buf_half2 = reinterpret_cast<T2 *>(qk_buf_);
const T2 *attr_mask_half2 = (const T2 *)attr_mask;
for (int64_t seq_id = static_cast<int64_t>(blockIdx.x); seq_id < seq_len;
seq_id += static_cast<int64_t>(gridDim.x) * NUM) {
T2 data[NUM][ITEMS_PER_THREAD];
int64_t qk_offset[NUM];
__shared__ float s_sum[NUM], s_max[NUM];
float local_max[NUM];
#pragma unroll
for (int j = 0; j < NUM; j++) {
local_max[j] = -1e20f;
}
for (int i = 0;
static_cast<int64_t>(blockDim.x) * i + threadIdx.x < (seq_len / 2) &&
i < ITEMS_PER_THREAD;
i++) {
int64_t mask_offset[NUM];
#pragma unroll
for (int j = 0; j < NUM; j++) {
qk_offset[j] =
((static_cast<int64_t>(blockIdx.y) * head_num +
static_cast<int64_t>(blockIdx.z)) *
static_cast<int64_t>(seq_len) +
seq_id +
static_cast<int64_t>(j) * static_cast<int64_t>(gridDim.x)) *
static_cast<int64_t>(seq_len / 2) +
static_cast<int64_t>(blockDim.x) * i + threadIdx.x;
mask_offset[j] =
(static_cast<int64_t>(blockIdx.y) * seq_len + seq_id +
static_cast<int64_t>(j) * static_cast<int64_t>(gridDim.x)) *
static_cast<int64_t>(seq_len / 2) +
static_cast<int64_t>(blockDim.x) * i + threadIdx.x;
}
T2 mask_val[NUM];
#pragma unroll
for (int j = 0; j < NUM; j++) {
mask_val[j] = ldg(&attr_mask_half2[mask_offset[j]]);
}
T2 qk[NUM];
#pragma unroll
for (int j = 0; j < NUM; j++) {
qk[j] = qk_buf_half2[qk_offset[j]];
}
#pragma unroll
for (int j = 0; j < NUM; j++) {
mask_val[j] = hmul2<T2>(hsub2<T2>(float2type2<T2>(1.0f), mask_val[j]),
float2type2<T2>(-10000.0f));
}
#pragma unroll
for (int j = 0; j < NUM; j++) {
data[j][i] = hadd2<T2>(qk[j], mask_val[j]);
local_max[j] = fmax(local_max[j],
fmax(static_cast<float>(data[j][i].x),
static_cast<float>(data[j][i].y)));
}
}
if (blockDim.x <= WARP_SIZE) {
funcs::WarpReduceMaxV2<float, NUM>(local_max);
} else {
funcs::BlockReduceMaxV2<float, NUM>(local_max);
}
if (threadIdx.x == 0) {
#pragma unroll
for (int j = 0; j < NUM; j++) {
s_max[j] = local_max[j];
}
}
__syncthreads();
float local_sum[NUM];
#pragma unroll
for (int j = 0; j < NUM; j++) {
local_sum[j] = {0.f};
}
for (int i = 0;
static_cast<int64_t>(blockDim.x) * i + threadIdx.x < (seq_len / 2) &&
i < ITEMS_PER_THREAD;
i++) {
#pragma unroll
for (int j = 0; j < NUM; j++) {
data[j][i] =
hexp2<T2>(hsub2<T2>(data[j][i], float2type2<T2>(s_max[j])));
}
#pragma unroll
for (int j = 0; j < NUM; j++) {
local_sum[j] += static_cast<float>(data[j][i].x + data[j][i].y);
}
}
if (blockDim.x <= WARP_SIZE) {
funcs::WarpReduceSumV2<float, NUM>(local_sum);
} else {
funcs::BlockReduceSumV2<float, NUM>(local_sum);
}
if (threadIdx.x == 0) {
#pragma unroll
for (int j = 0; j < NUM; j++) {
s_sum[j] = __fdividef(1.0f, local_sum[j] + 1e-6f);
}
}
__syncthreads();
for (int i = 0;
static_cast<int64_t>(blockDim.x) * i + threadIdx.x < (seq_len / 2) &&
i < ITEMS_PER_THREAD;
i++) {
#pragma unroll
for (int j = 0; j < NUM; j++) {
qk_offset[j] =
((static_cast<int64_t>(blockIdx.y) * head_num +
static_cast<int64_t>(blockIdx.z)) *
static_cast<int64_t>(seq_len) +
seq_id +
static_cast<int64_t>(j) * static_cast<int64_t>(gridDim.x)) *
static_cast<int64_t>(seq_len / 2) +
static_cast<int64_t>(blockDim.x) * i + threadIdx.x;
}
#pragma unroll
for (int j = 0; j < NUM; j++) {
qk_buf_half2[qk_offset[j]] =
hmul2<T2>(data[j][i], float2type2<T2>(s_sum[j]));
}
}
}
}
#define SOFTMAX_KERNEL_WITH_MASK(REPEAT_THREAD) \
do { \
block.x /= REPEAT_THREAD; \
grid.x /= 4; \
constexpr int NUM = 4; \
softmax_kernel_with_mask<half, REPEAT_THREAD, NUM> \
<<<grid, block, 0, stream>>>(reinterpret_cast<half *>(qk_buf_), \
(const half *)bias_qk, \
batch_size, \
head_num, \
seq_len); \
} while (0)
template <typename T>
inline void MatmulWithHeadQK(const GPUContext &dev_ctx,
int head_num,
int seq_len,
int size_per_head,
int batch_size,
bool q_trans,
bool k_trans,
T *q_buf_,
T *k_buf_,
T *qk_buf_,
const T *bias_qk,
bool bias_is_mask,
T alpha,
T beta) {
CBLAS_TRANSPOSE transA = !q_trans ? CblasNoTrans : CblasTrans;
CBLAS_TRANSPOSE transB = !k_trans ? CblasNoTrans : CblasTrans;
typedef typename CUDATypeTraits<T>::TYPE run_type;
auto blas = funcs::GetBlas<GPUContext, run_type>(dev_ctx);
auto stream = dev_ctx.stream();
blas.BatchedGEMM(transA,
transB,
seq_len,
seq_len,
size_per_head,
static_cast<run_type>(alpha),
reinterpret_cast<run_type *>(q_buf_),
reinterpret_cast<run_type *>(k_buf_),
static_cast<run_type>(beta),
reinterpret_cast<run_type *>(qk_buf_),
static_cast<int64_t>(batch_size) * head_num,
static_cast<int64_t>(seq_len) * size_per_head,
static_cast<int64_t>(seq_len) * size_per_head);
if (seq_len <= 1024) {
int64_t grid_size64 = static_cast<int64_t>(batch_size) * head_num * seq_len;
PADDLE_ENFORCE_LE_INT_MAX(
grid_size64, "CUDA launch grid batch_size * head_num * seq_len");
int grid_size = static_cast<int>(grid_size64);
int block = seq_len;
// Align block to 32, also limit seq_len to max block size.
if (seq_len % 2 == 0) {
block =
(seq_len <= (2 * WARP_SIZE))
? WARP_SIZE
: ((seq_len + (2 * WARP_SIZE - 1)) / (2 * WARP_SIZE)) * WARP_SIZE;
if (std::is_same<T, float>::value) {
SoftmaxKernelWithEltadd2<float2><<<grid_size, block, 0, stream>>>(
reinterpret_cast<float2 *>(qk_buf_),
reinterpret_cast<const float2 *>(bias_qk),
batch_size,
head_num,
seq_len / 2,
FINAL_MASK);
} else {
if (bias_is_mask) {
#if defined(__HIPCC__) || (defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 700)
PADDLE_ENFORCE_EQ(bias_is_mask,
false,
common::errors::InvalidArgument(
"QK_bias is mask can't be supported on rocm or "
"cuda_arch<700"));
#else
dim3 grid(seq_len, batch_size, head_num);
dim3 block((seq_len / 2 + WARP_SIZE - 1) / WARP_SIZE * WARP_SIZE);
SOFTMAX_KERNEL_WITH_MASK(1);
#endif
} else {
SoftmaxKernelWithEltadd2<__half2><<<grid_size, block, 0, stream>>>(
reinterpret_cast<__half2 *>(qk_buf_),
reinterpret_cast<const __half2 *>(bias_qk),
batch_size,
head_num,
seq_len / 2,
FINAL_MASK);
}
}
} else {
block = (seq_len <= WARP_SIZE)
? WARP_SIZE
: ((seq_len + WARP_SIZE - 1) / WARP_SIZE) * WARP_SIZE;
SoftmaxKernelWithEltadd<T><<<grid_size, block, 0, stream>>>(
qk_buf_, bias_qk, batch_size, head_num, seq_len, FINAL_MASK);
}
} else {
int64_t grid_size64 = static_cast<int64_t>(batch_size) * head_num * seq_len;
PADDLE_ENFORCE_LE_INT_MAX(
grid_size64, "CUDA launch grid batch_size * head_num * seq_len");
int grid_size = static_cast<int>(grid_size64);
int block = 512;
if (seq_len % 2 == 0) {
if (std::is_same<T, float>::value) {
SoftmaxKernelWithEltaddForLarge2<float2>
<<<grid_size, block, 0, stream>>>(
reinterpret_cast<float2 *>(qk_buf_),
reinterpret_cast<const float2 *>(bias_qk),
batch_size,
head_num,
seq_len / 2,
FINAL_MASK);
} else {
if (bias_is_mask) {
#if defined(__HIPCC__) || (defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 700)
PADDLE_ENFORCE_EQ(bias_is_mask,
false,
common::errors::InvalidArgument(
"QK_bias is mask can't be supported on rocm or "
"cuda_arch<700"));
#else
dim3 grid(seq_len, batch_size, head_num);
dim3 block((seq_len / 2 + WARP_SIZE - 1) / WARP_SIZE * WARP_SIZE);
if (block.x > 0 && block.x <= 1024) {
SOFTMAX_KERNEL_WITH_MASK(1);
} else if (block.x <= 2048) {
SOFTMAX_KERNEL_WITH_MASK(2);
} else if (block.x <= 4096) {
SOFTMAX_KERNEL_WITH_MASK(4);
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"Cannot support the length of attention > 8192."));
}
#endif
} else {
SoftmaxKernelWithEltaddForLarge2<__half2>
<<<grid_size, block, 0, stream>>>(
reinterpret_cast<__half2 *>(qk_buf_),
reinterpret_cast<const __half2 *>(bias_qk),
batch_size,
head_num,
seq_len / 2,
FINAL_MASK);
}
}
} else {
SoftmaxKernelWithEltaddForLarge<T><<<grid_size, block, 0, stream>>>(
qk_buf_, bias_qk, batch_size, head_num, seq_len, FINAL_MASK);
}
}
}
template <typename T>
inline void MatmulWithHeadQKV(const GPUContext &dev_ctx,
int head_num,
int seq_len,
int size_per_head,
int batch_size,
bool qk_trans,
bool v_trans,
T *v_buf_,
const T *qk_buf_,
T *dst,
T alpha,
T beta) {
int64_t m = static_cast<int64_t>(batch_size) * seq_len;
int64_t k = static_cast<int64_t>(head_num) * size_per_head;
typedef typename CUDATypeTraits<T>::TYPE run_type;
auto blas = funcs::GetBlas<GPUContext, run_type>(dev_ctx);
auto stream = dev_ctx.stream();
CBLAS_TRANSPOSE transA = !qk_trans ? CblasNoTrans : CblasTrans;
CBLAS_TRANSPOSE transB = !v_trans ? CblasNoTrans : CblasTrans;
blas.BatchedGEMM(transA,
transB,
seq_len,
size_per_head,
seq_len,
static_cast<run_type>(alpha),
reinterpret_cast<const run_type *>(qk_buf_),
reinterpret_cast<run_type *>(v_buf_),
static_cast<run_type>(beta),
reinterpret_cast<run_type *>(dst),
static_cast<int64_t>(batch_size) * head_num,
static_cast<int64_t>(seq_len) * seq_len,
static_cast<int64_t>(seq_len) * size_per_head);
}
template <typename T>
void MultiheadGPUComputeFunctor<T>::operator()(const GPUContext &dev_ctx,
int batch,
int seq_len,
int head_num,
int head_size,
T *qkptr,
const T *bias_qk_ptr,
bool bias_is_mask,
T *tptr,
T alpha,
T beta) {
auto stream = dev_ctx.stream();
const int64_t tsize =
static_cast<int64_t>(batch) * head_num * seq_len * head_size;
T *qptr = tptr;
T *kptr = qptr + tsize;
T *vptr = kptr + tsize;
// batch gemm stride, softmaxwithscale.
MatmulWithHeadQK<T>(dev_ctx,
head_num,
seq_len,
head_size,
batch,
false,
true,
qptr,
kptr,
qkptr,
bias_qk_ptr,
bias_is_mask,
alpha,
beta);
// batch gemm stride, transpose.
MatmulWithHeadQKV<T>(dev_ctx,
head_num,
seq_len,
head_size,
batch,
false,
false,
vptr,
qkptr,
tptr,
T(1.0),
beta);
}
template class PADDLE_API MultiheadGPUComputeFunctor<float>;
// device function 'operator()' is not supported until cuda 10.0
// HIP defined __HIP_NO_HALF_CONVERSIONS__ in hip.cmake
#if defined(PADDLE_WITH_CUDA)
template class PADDLE_API MultiheadGPUComputeFunctor<half>;
#endif
} // namespace funcs
} // namespace phi