Files
2026-07-13 12:40:42 +08:00

750 lines
28 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 "glog/logging.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/kernels/funcs/blas/blas.h"
#include "paddle/phi/kernels/funcs/broadcast_function.h"
#include "paddle/phi/kernels/funcs/concat_and_split_functor.h"
#include "paddle/phi/kernels/funcs/dropout_impl.cu.h"
#include "paddle/phi/kernels/funcs/elementwise_base.h"
#include "paddle/phi/kernels/funcs/elementwise_functor.h"
#include "paddle/phi/kernels/funcs/functors.h"
#include "paddle/phi/kernels/funcs/transpose_function.cuh"
#include "paddle/phi/kernels/fusion/gpu/fused_softmax_mask_utils.h"
#include "paddle/phi/kernels/gpudnn/softmax_gpudnn.h"
namespace phi {
namespace fusion {
class AttnDropoutParam {
public:
AttnDropoutParam() {
is_test_ = false;
dropout_implementation_ = "downgrade_in_infer";
dropout_prob_ = 0.5;
is_upscale_in_train_ = false;
is_fix_seed_ = false;
seed_val_ = 0;
seed_ = nullptr;
}
AttnDropoutParam(bool is_test,
const std::string dropout_implementation,
float dropout_prob,
bool is_upscale_in_train,
bool is_fix_seed,
int seed_val,
const DenseTensor* seed) {
is_test_ = is_test;
dropout_implementation_ = dropout_implementation;
dropout_prob_ = dropout_prob;
is_upscale_in_train_ = is_upscale_in_train;
is_fix_seed_ = is_fix_seed;
seed_val_ = seed_val;
seed_ = seed;
}
bool is_test_;
std::string dropout_implementation_;
float dropout_prob_;
bool is_upscale_in_train_;
bool is_fix_seed_;
int seed_val_;
const DenseTensor* seed_;
};
template <typename T, int VecSize>
__global__ void TransposeRemovingPadding(const T* input_data,
T* output_data,
const int batch_size,
const int num_head,
const int seq_len,
const int head_dim,
const int token_num,
const int elem_cnt,
const int* padding_offset) {
// transpose and remove padding
// [batch_size, num_head, seq_len, head_dim] -> [token_num, num_head,
// head_dim]
int64_t idx =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(blockIdx.x) +
static_cast<int64_t>(threadIdx.x);
const int dim_embed = num_head * head_dim;
using LoadT = AlignedVector<T, VecSize>;
LoadT src_vec;
for (int32_t linear_index = idx * VecSize,
step = gridDim.x * blockDim.x * VecSize;
linear_index < elem_cnt;
linear_index += step) {
const int token_idx = linear_index / dim_embed;
const int ori_token_idx =
token_idx + (padding_offset == nullptr ? 0 : padding_offset[token_idx]);
const int ori_batch_id = ori_token_idx / seq_len;
const int ori_seq_id = ori_token_idx % seq_len;
const int ori_head_id = (linear_index % dim_embed) / head_dim;
const int ori_head_lane = (linear_index % dim_embed) % head_dim;
const int ori_idx = ori_batch_id * num_head * seq_len * head_dim +
ori_head_id * seq_len * head_dim +
ori_seq_id * head_dim + ori_head_lane;
Load<T, VecSize>(&input_data[ori_idx], &src_vec);
Store<T, VecSize>(src_vec, &output_data[linear_index]);
}
}
template <typename T>
void InvokeTransposeRemovePadding(const GPUContext& dev_ctx,
const T* input_data,
T* output_data,
const int batch_size,
const int num_head,
const int seq_len,
const int head_dim,
const int token_num,
const int* padding_offset) {
// [batch_size, num_head, seq_len, head_dim] -> [token_num, num_head,
// head_dim]
constexpr int VEC_16B = 16;
const int elem_cnt = token_num * num_head * head_dim;
constexpr int PackSize = VEC_16B / sizeof(T);
PADDLE_ENFORCE_EQ(
head_dim % PackSize,
0,
errors::PreconditionNotMet(
"dim_head=%d must be divisible by vec_size=%d", head_dim, PackSize));
const int32_t pack_num = elem_cnt / PackSize;
const int32_t block_size = 128;
int32_t grid_size = (pack_num + block_size - 1) / block_size;
TransposeRemovingPadding<T, PackSize>
<<<grid_size, block_size, 0, dev_ctx.stream()>>>(input_data,
output_data,
batch_size,
num_head,
seq_len,
head_dim,
token_num,
elem_cnt,
padding_offset);
}
template <typename T>
class FMHARef {
public:
FMHARef(const GPUContext& dev_ctx,
int64_t batch_size,
int64_t seq_len,
int64_t num_head,
int64_t head_dim,
AttnDropoutParam param)
: dev_ctx_(dev_ctx),
batch_size_(batch_size),
seq_len_(seq_len),
num_head_(num_head),
head_dim_(head_dim),
dropout_param_(param) {}
~FMHARef() {}
void ComputeForward(const DenseTensor& qkv_input_tensor,
const DenseTensor* cache_kv_tensor,
const DenseTensor* src_mask_tensor,
DenseTensor* transpose_2_out_tensor,
DenseTensor* cache_kv_out_tensor,
DenseTensor* qk_out_tensor,
DenseTensor* src_mask_out_tensor,
DenseTensor* softmax_out_tensor,
DenseTensor* dropout_mask_out_tensor,
DenseTensor* dropout_out_tensor,
DenseTensor* qktv_out_tensor,
DenseTensor* fmha_out_tensor) {
// input shape: [bs, seq_len, 3, num_head, head_dim]
// transpose with perm [2, 0, 3, 1, 4],
// output_shape: [3, bs, num_head, seq_len, head_dim]
std::vector<int> perm_1 = {2, 0, 3, 1, 4};
funcs::TransposeGPUKernelDriver<T>(
dev_ctx_, qkv_input_tensor, perm_1, transpose_2_out_tensor);
T* qkv_data = transpose_2_out_tensor->data<T>();
T* qk_out_data = qk_out_tensor->data<T>();
T* qktv_out_data = qktv_out_tensor->data<T>();
T* softmax_out_data = softmax_out_tensor->data<T>();
T* fmha_out_data = fmha_out_tensor->data<T>();
auto out_seq_len = seq_len_;
if (cache_kv_tensor) {
// kv [2, bs, num_head, seq_len, head_dim]
auto kv_tensor = transpose_2_out_tensor->Slice(1, 3);
funcs::ConcatFunctor<GPUContext, T> concat;
// out [2, bs, num_head, cache_seq_len + seq_len, head_dim]
concat(dev_ctx_, {*cache_kv_tensor, kv_tensor}, 3, cache_kv_out_tensor);
out_seq_len = cache_kv_out_tensor->dims()[3];
}
int64_t q_size = batch_size_ * seq_len_ * num_head_ * head_dim_;
T* q_ptr = qkv_data;
T* k_ptr = nullptr;
T* v_ptr = nullptr;
if (cache_kv_tensor) {
int64_t k_size = cache_kv_out_tensor->numel() / 2;
k_ptr = cache_kv_out_tensor->data<T>();
v_ptr = k_ptr + k_size;
} else {
int64_t k_size = q_size;
k_ptr = q_ptr + q_size;
v_ptr = k_ptr + k_size;
}
{
// NOTE(wangxi): We scale Q with 1/sqrt(Dh) before QK^T, because for
// float16 calculation, INF may appear in QK^T if we do not scale before.
float alpha = 1.0 / sqrt(head_dim_);
auto q_tensor = transpose_2_out_tensor->Slice(0, 1);
auto functor = funcs::ScaleFunctor<T>(alpha);
std::vector<const DenseTensor*> ins = {&q_tensor};
std::vector<DenseTensor*> outs = {&q_tensor};
funcs::ElementwiseKernel<T>(dev_ctx_, ins, &outs, functor);
}
// q*k^t, batched_gemm
CBLAS_TRANSPOSE transA = CblasNoTrans;
CBLAS_TRANSPOSE transB = CblasTrans;
auto blas = funcs::GetBlas<GPUContext, T>(dev_ctx_);
int gemm_batch_size = batch_size_ * num_head_;
int gemm_m = seq_len_;
int gemm_n = out_seq_len;
int gemm_k = head_dim_;
T alpha = static_cast<T>(1.0);
T beta = static_cast<T>(0.0);
int64_t stride_a = gemm_m * gemm_k;
int64_t stride_b = gemm_k * gemm_n;
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
q_ptr,
k_ptr,
beta,
qk_out_data,
gemm_batch_size,
stride_a,
stride_b);
int softmax_axis = -1;
if (src_mask_tensor != nullptr) {
if (src_mask_out_tensor == nullptr && seq_len_ == out_seq_len) {
phi::fusion::LaunchFusedSoftmaxMaskKernel<T>(qk_out_data,
src_mask_tensor->data<T>(),
softmax_out_data,
batch_size_,
num_head_,
seq_len_,
dev_ctx_.stream());
} else {
std::vector<const DenseTensor*> ins;
std::vector<DenseTensor*> outs;
ins.emplace_back(qk_out_tensor);
ins.emplace_back(src_mask_tensor);
outs.emplace_back(src_mask_out_tensor);
int elewise_add_axis = -1;
funcs::BroadcastKernel<T>(
dev_ctx_, ins, &outs, funcs::AddFunctor<T>(), elewise_add_axis);
phi::SoftmaxForwardCUDAKernelDriver<T>(
dev_ctx_, *src_mask_out_tensor, softmax_axis, softmax_out_tensor);
}
} else {
phi::SoftmaxForwardCUDAKernelDriver<T>(
dev_ctx_, *qk_out_tensor, softmax_axis, softmax_out_tensor);
}
transB = CblasNoTrans;
gemm_m = seq_len_;
gemm_n = head_dim_;
gemm_k = out_seq_len;
alpha = static_cast<T>(1.0);
stride_a = gemm_m * gemm_k;
stride_b = gemm_k * gemm_n;
if (dropout_param_.dropout_prob_) {
funcs::DropoutFwGPUKernelDriver<T>(
static_cast<const GPUContext&>(dev_ctx_),
dropout_param_.is_test_,
dropout_param_.dropout_prob_,
dropout_param_.is_upscale_in_train_,
dropout_param_.is_fix_seed_,
dropout_param_.seed_val_,
static_cast<const DenseTensor&>(*softmax_out_tensor),
dropout_param_.seed_,
dropout_mask_out_tensor,
dropout_out_tensor,
false);
T* dropout_out_data = dropout_out_tensor->data<T>();
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
dropout_out_data,
v_ptr,
beta,
qktv_out_data,
gemm_batch_size,
stride_a,
stride_b);
} else {
// softmax_out * v, batched_gemm
// output shape: [batch_size, num_heads, seq_len, head_dim]
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
softmax_out_data,
v_ptr,
beta,
qktv_out_data,
gemm_batch_size,
stride_a,
stride_b);
}
// transpose: [0, 2, 1, 3]
// output shape: [batch_size, seq_len, num_heads, head_dim]
std::vector<int> perm_3 = {0, 2, 1, 3};
funcs::TransposeGPUKernelDriver<T>(
dev_ctx_, *qktv_out_tensor, perm_3, fmha_out_tensor);
}
void ComputeForwardWithoutTranspose(const DenseTensor* cache_kv_tensor,
const DenseTensor* src_mask_tensor,
const DenseTensor* padding_offset_tensor,
DenseTensor* q_transpose_out_tensor,
DenseTensor* kv_transpose_out_tensor,
DenseTensor* cache_kv_out_tensor,
DenseTensor* qk_out_tensor,
DenseTensor* src_mask_out_tensor,
DenseTensor* softmax_out_tensor,
DenseTensor* dropout_mask_out_tensor,
DenseTensor* dropout_out_tensor,
DenseTensor* qktv_out_tensor,
DenseTensor* fmha_out_tensor,
const int token_num) {
// input shape: [bs, seq_len, 3, num_head, head_dim]
// transpose with perm [2, 0, 3, 1, 4],
// output_shape: [3, bs, num_head, seq_len, head_dim]
T* qk_out_data = qk_out_tensor->data<T>();
T* qktv_out_data = qktv_out_tensor->data<T>();
T* softmax_out_data = softmax_out_tensor->data<T>();
T* dropout_out_data = dropout_out_tensor->data<T>();
T* fmha_out_data = fmha_out_tensor->data<T>();
auto out_seq_len = seq_len_;
if (cache_kv_tensor) {
// kv [2, bs, num_head, seq_len, head_dim]
funcs::ConcatFunctor<GPUContext, T> concat;
// out [2, bs, num_head, cache_seq_len + seq_len, head_dim]
concat(dev_ctx_,
{*cache_kv_tensor, *kv_transpose_out_tensor},
3,
cache_kv_out_tensor);
out_seq_len = cache_kv_out_tensor->dims()[3];
}
int64_t q_size = batch_size_ * seq_len_ * num_head_ * head_dim_;
T* q_ptr = q_transpose_out_tensor->data<T>();
T* k_ptr = nullptr;
T* v_ptr = nullptr;
if (cache_kv_tensor) {
int64_t k_size = cache_kv_out_tensor->numel() / 2;
k_ptr = cache_kv_out_tensor->data<T>();
v_ptr = k_ptr + k_size;
} else {
int64_t k_size = q_size;
k_ptr = kv_transpose_out_tensor->data<T>();
v_ptr = k_ptr + k_size;
}
{
// NOTE(wangxi): We scale Q with 1/sqrt(Dh) before QK^T, because for
// float16 calculation, INF may appear in QK^T if we do not scale before.
float alpha = 1.0 / sqrt(head_dim_);
auto functor = funcs::ScaleFunctor<T>(alpha);
std::vector<const DenseTensor*> ins = {q_transpose_out_tensor};
std::vector<DenseTensor*> outs = {q_transpose_out_tensor};
funcs::ElementwiseKernel<T>(dev_ctx_, ins, &outs, functor);
}
// q*k^t, batched_gemm
CBLAS_TRANSPOSE transA = CblasNoTrans;
CBLAS_TRANSPOSE transB = CblasTrans;
auto blas = funcs::GetBlas<GPUContext, T>(dev_ctx_);
int gemm_batch_size = batch_size_ * num_head_;
int gemm_m = seq_len_;
int gemm_n = out_seq_len;
int gemm_k = head_dim_;
T alpha = static_cast<T>(1.0);
T beta = static_cast<T>(0.0);
int64_t stride_a = gemm_m * gemm_k;
int64_t stride_b = gemm_k * gemm_n;
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
q_ptr,
k_ptr,
beta,
qk_out_data,
gemm_batch_size,
stride_a,
stride_b);
int softmax_axis = -1;
if (src_mask_tensor != nullptr) {
if (src_mask_out_tensor == nullptr && seq_len_ == out_seq_len) {
phi::fusion::LaunchFusedSoftmaxMaskKernel<T>(qk_out_data,
src_mask_tensor->data<T>(),
softmax_out_data,
batch_size_,
num_head_,
seq_len_,
dev_ctx_.stream());
} else {
std::vector<const DenseTensor*> ins;
std::vector<DenseTensor*> outs;
ins.emplace_back(qk_out_tensor);
ins.emplace_back(src_mask_tensor);
outs.emplace_back(src_mask_out_tensor);
int elewise_add_axis = -1;
funcs::BroadcastKernel<T>(
dev_ctx_, ins, &outs, funcs::AddFunctor<T>(), elewise_add_axis);
phi::SoftmaxForwardCUDAKernelDriver<T>(
dev_ctx_, *src_mask_out_tensor, softmax_axis, softmax_out_tensor);
}
} else {
phi::SoftmaxForwardCUDAKernelDriver<T>(
dev_ctx_, *qk_out_tensor, softmax_axis, softmax_out_tensor);
}
transB = CblasNoTrans;
gemm_m = seq_len_;
gemm_n = head_dim_;
gemm_k = out_seq_len;
alpha = static_cast<T>(1.0);
stride_a = gemm_m * gemm_k;
stride_b = gemm_k * gemm_n;
if (dropout_param_.dropout_prob_) {
funcs::DropoutFwGPUKernelDriver<T>(
static_cast<const GPUContext&>(dev_ctx_),
dropout_param_.is_test_,
dropout_param_.dropout_prob_,
dropout_param_.is_upscale_in_train_,
dropout_param_.is_fix_seed_,
dropout_param_.seed_val_,
static_cast<const DenseTensor&>(*softmax_out_tensor),
dropout_param_.seed_,
dropout_mask_out_tensor,
dropout_out_tensor,
false);
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
dropout_out_data,
v_ptr,
beta,
qktv_out_data,
gemm_batch_size,
stride_a,
stride_b);
} else {
// softmax_out * v, batched_gemm
// output shape: [batch_size, num_heads, seq_len, head_dim]
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
softmax_out_data,
v_ptr,
beta,
qktv_out_data,
gemm_batch_size,
stride_a,
stride_b);
}
// transpose: [0, 2, 1, 3]
// output shape: [batch_size, seq_len, num_heads, head_dim]
if (!padding_offset_tensor) {
std::vector<int> perm_3 = {0, 2, 1, 3};
funcs::TransposeGPUKernelDriver<T>(
dev_ctx_, *qktv_out_tensor, perm_3, fmha_out_tensor);
} else {
InvokeTransposeRemovePadding<T>(dev_ctx_,
qktv_out_data,
fmha_out_data,
batch_size_,
num_head_,
seq_len_,
head_dim_,
token_num,
padding_offset_tensor->data<int>());
}
}
void ComputeBackward(const DenseTensor& transpose_2_out_tensor,
const DenseTensor* src_mask_tensor,
const DenseTensor& softmax_out_tensor,
const DenseTensor& dropout_mask_out_tensor,
const DenseTensor& dropout_out_tensor,
const DenseTensor& qk_out_tensor,
const DenseTensor& src_mask_out_tensor,
const DenseTensor& fmha_out_grad_tensor,
DenseTensor* qktv_out_grad_tensor,
DenseTensor* dropout_out_grad_tensor,
DenseTensor* softmax_out_grad_tensor,
DenseTensor* src_mask_out_grad_tensor,
DenseTensor* qk_out_grad_tensor,
DenseTensor* transpose_2_out_grad_tensor,
DenseTensor* src_mask_grad_tensor,
DenseTensor* qkv_input_grad_tensor) {
auto blas = funcs::GetBlas<GPUContext, T>(dev_ctx_);
int q_size = batch_size_ * seq_len_ * num_head_ * head_dim_;
int k_size = q_size;
int softmax_axis = -1;
T* qkv_grad_data = transpose_2_out_grad_tensor->data<T>();
T* q_grad_ptr = qkv_grad_data;
T* k_grad_ptr = q_grad_ptr + q_size;
T* v_grad_ptr = k_grad_ptr + k_size;
const T* qkv_data = transpose_2_out_tensor.data<T>();
const T* q_ptr = qkv_data;
const T* k_ptr = q_ptr + q_size;
const T* v_ptr = k_ptr + k_size;
const T* softmax_out_data = softmax_out_tensor.data<T>();
T* softmax_out_grad_data = softmax_out_grad_tensor->data<T>();
T* qktv_out_grad_data = qktv_out_grad_tensor->data<T>();
// transpose bw
std::vector<int> perm_3 = {0, 2, 1, 3};
funcs::TransposeGPUKernelDriver<T>(
dev_ctx_, fmha_out_grad_tensor, perm_3, qktv_out_grad_tensor);
// recall batchedgemm(nn) fw: softmax_out_data(x) * v_ptr(y) =
// qktv_out_data(out)
CBLAS_TRANSPOSE transA = CblasTrans;
CBLAS_TRANSPOSE transB = CblasNoTrans;
int gemm_batch_size = batch_size_ * num_head_;
int gemm_m = seq_len_;
int gemm_n = head_dim_;
int gemm_k = seq_len_;
T alpha = static_cast<T>(1.0);
T beta = static_cast<T>(0.0);
int64_t stride_a = gemm_m * gemm_k;
int64_t stride_b = gemm_k * gemm_n;
// bw: dy = x^t * dout
if (dropout_param_.dropout_prob_) {
const T* dropout_out_data = dropout_out_tensor.data<T>();
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
dropout_out_data,
qktv_out_grad_data,
beta,
v_grad_ptr,
gemm_batch_size,
stride_a,
stride_b);
} else {
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
softmax_out_data,
qktv_out_grad_data,
beta,
v_grad_ptr,
gemm_batch_size,
stride_a,
stride_b);
}
// bw: dx = dout * y^t
transA = CblasNoTrans;
transB = CblasTrans;
gemm_m = seq_len_;
gemm_n = seq_len_;
gemm_k = head_dim_;
stride_a = gemm_m * gemm_k;
stride_b = gemm_k * gemm_n;
if (dropout_param_.dropout_prob_) {
T* dropout_out_grad_data = dropout_out_grad_tensor->data<T>();
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
qktv_out_grad_data,
v_ptr,
beta,
dropout_out_grad_data,
gemm_batch_size,
stride_a,
stride_b);
} else {
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
qktv_out_grad_data,
v_ptr,
beta,
softmax_out_grad_data,
gemm_batch_size,
stride_a,
stride_b);
}
// dropout bw
if (dropout_param_.dropout_prob_) {
funcs::DropoutGradGPUKernelDriver<T>(
static_cast<const GPUContext&>(dev_ctx_),
false,
dropout_param_.dropout_prob_,
dropout_param_.is_upscale_in_train_,
static_cast<const DenseTensor&>(*dropout_out_grad_tensor),
dropout_mask_out_tensor,
softmax_out_grad_tensor,
false);
}
if (src_mask_tensor != nullptr) {
phi::SoftmaxBackwardCUDAKernelDriver<T>(dev_ctx_,
softmax_out_tensor,
*softmax_out_grad_tensor,
softmax_axis,
src_mask_out_grad_tensor);
// recall LaunchElementwiseCudaKernel fw: src_mask_out = qk_out +
// src_mask
// Special case when dy is not needed and dx doesn't reduce
if (qk_out_grad_tensor != nullptr && src_mask_grad_tensor == nullptr &&
qk_out_tensor.dims() == src_mask_out_tensor.dims()) {
VLOG(4) << "Special case when dy is not needed and dx doesn't "
"reduce";
phi::Copy(dev_ctx_,
*src_mask_out_grad_tensor,
dev_ctx_.GetPlace(),
false,
qk_out_grad_tensor);
} else {
PADDLE_THROW(errors::InvalidArgument(
"Only used for the backward elementwise_add op when "
"dy is not needed and dx is not reduce"));
return;
}
} else {
phi::SoftmaxBackwardCUDAKernelDriver<T>(dev_ctx_,
softmax_out_tensor,
*softmax_out_grad_tensor,
softmax_axis,
qk_out_grad_tensor);
}
T* qk_out_grad_data = qk_out_grad_tensor->data<T>();
// NOTE(wangxi): For we scale Q with 1/sqrt(Dh) in forward, so we set
// alpha = 1.0 in backward.
alpha = static_cast<T>(1.0);
// recall batchedgemm(nt) fw: q_ptr * (k_ptr)^t = qk_out
// bw: dy (seq_len * head_dim) = (dout)^t * x
transA = CblasTrans;
transB = CblasNoTrans;
gemm_m = seq_len_;
gemm_n = head_dim_;
gemm_k = seq_len_;
stride_a = gemm_m * gemm_k;
stride_b = gemm_k * gemm_n;
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
qk_out_grad_data,
q_ptr,
beta,
k_grad_ptr,
gemm_batch_size,
stride_a,
stride_b);
// dx (seq_len * head_dim) = dout * y
alpha = static_cast<T>(1.0 / sqrt(head_dim_));
transA = CblasNoTrans;
transB = CblasNoTrans;
gemm_m = seq_len_;
gemm_n = head_dim_;
gemm_k = seq_len_;
stride_a = gemm_m * gemm_k;
stride_b = gemm_k * gemm_n;
blas.BatchedGEMM(transA,
transB,
gemm_m,
gemm_n,
gemm_k,
alpha,
qk_out_grad_data,
k_ptr,
beta,
q_grad_ptr,
gemm_batch_size,
stride_a,
stride_b);
// transpose bw
std::vector<int> perm_1 = {1, 3, 0, 2, 4};
funcs::TransposeGPUKernelDriver<T>(
dev_ctx_, *transpose_2_out_grad_tensor, perm_1, qkv_input_grad_tensor);
}
private:
const GPUContext& dev_ctx_;
int64_t batch_size_;
int64_t seq_len_;
int64_t num_head_;
int64_t head_dim_;
AttnDropoutParam dropout_param_;
};
} // namespace fusion
} // namespace phi