// 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. #include "paddle/common/enforce.h" #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/fusion/gpu/fused_dropout_add_utils.h" #include "paddle/phi/kernels/funcs/dropout_impl_util.h" #include "paddle/phi/kernels/funcs/functors.h" #include "paddle/phi/kernels/primitive/compute_primitives.h" #include "paddle/phi/kernels/funcs/dropout_impl.cu.h" namespace phi { namespace fusion { template struct NoMaskFwFunctor { const float retain_prob_; const bool is_upscale_in_train_; using MT = typename phi::dtype::MPTypeTrait::Type; MT factor; HOSTDEVICE inline NoMaskFwFunctor(const float retain_prob, const bool is_upscale_in_train) : retain_prob_(retain_prob), is_upscale_in_train_(is_upscale_in_train) { factor = static_cast(1.0f / retain_prob_); } HOSTDEVICE inline void operator()(OutT* dst, const T1* src_val, const T2* rand, int num) const { static constexpr int kCount = funcs::uniform_distribution::kReturnsCount; #pragma unroll for (int i = 0; i < kCount; i++) { if (rand[i] < retain_prob_) { dst[i] = is_upscale_in_train_ ? static_cast(static_cast(src_val[i]) * factor) : static_cast(src_val[i]); dst[i] += src_val[i + kCount]; } else { dst[i] = src_val[i + kCount]; } } } }; template struct ScaleAddFuctor { using MT = typename phi::dtype::MPTypeTrait::Type; explicit ScaleAddFuctor(const MT factor, bool upscale_in_train) : factor_(factor), upscale_in_train_(upscale_in_train) {} __device__ __forceinline__ T operator()(const T src, const T res) const { return upscale_in_train_ ? src + res : static_cast(static_cast(src) * factor_) + res; } private: MT factor_; bool upscale_in_train_; }; template __global__ void VectorizedDropoutForward( /* This is used to relate kernel to cudaGraph nodes*/ unsigned int identifier, const size_t n, uint64_t seed, const T* src, const T* res, T* dst, uint64_t increment, size_t main_offset, Functor functor) { size_t idx = static_cast(BLOCK_ID_X * BLOCK_NUM_X); static constexpr int kCount = funcs::uniform_distribution::kReturnsCount; size_t stride = BLOCK_NUM_X * GRID_NUM_X * kCount; #ifdef PADDLE_WITH_HIP hiprandStatePhilox4_32_10_t state; hiprand_init(seed, idx + THREAD_ID_X, increment, &state); using SType = hiprandStatePhilox4_32_10_t; #else curandStatePhilox4_32_10_t state; curand_init(seed, idx + THREAD_ID_X, increment, &state); using SType = curandStatePhilox4_32_10_t; #endif T dst_res[kCount * 2]; float rands[kCount]; using Rand = funcs::uniform_distribution; int deal_size = BLOCK_NUM_X * kCount; size_t fix = idx * kCount; for (; fix < main_offset; fix += stride) { kps::ReadData(&dst_res[0], src + fix, deal_size); kps::ReadData(&dst_res[kCount], res + fix, deal_size); kps::ElementwiseRandom( &rands[0], Rand(), &state); // dst kps::OperatorTernary( &dst_res[0], &dst_res[0], &rands[0], functor, kCount); kps::WriteData(dst + fix, &dst_res[0], deal_size); if (fix > idx * kCount + 1) { __syncthreads(); } } int remainder = n - fix; if (remainder > 0) { kps::ReadData(&dst_res[0], src + fix, remainder); kps::ReadData(&dst_res[kCount], res + fix, remainder); kps::ElementwiseRandom( &rands[0], Rand(), &state); // dst kps::OperatorTernary( &dst_res[0], &dst_res[0], &rands[0], functor, kCount); kps::WriteData(dst + fix, &dst_res[0], remainder); __syncthreads(); } } template void FusedDropoutAddKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& y, const optional& seed_tensor, const Scalar& p, bool is_test, const std::string& mode, int seed, bool fix_seed, DenseTensor* out, DenseTensor* seed_offset) { auto* out_data = dev_ctx.template Alloc(out); auto* seed_offset_data = dev_ctx.template HostAlloc(seed_offset); int64_t numel = x.numel(); auto stream = dev_ctx.stream(); bool upscale_in_train = (mode == "upscale_in_train"); const auto* x_data = x.data(); const auto* y_data = y.data(); float dropout_rate = p.to(); if (!is_test) { if (dropout_rate == 1.0f) { phi::Copy(dev_ctx, y, dev_ctx.GetPlace(), false, out); return; } uint64_t seed_data; uint64_t increment; auto random_prop = GetRandomCudaProp(numel, dev_ctx); size_t grid_size_64 = random_prop[0]; size_t block_size_64 = random_prop[1]; PADDLE_ENFORCE_LE_UINT32_MAX(grid_size_64, "fused_dropout_add grid.x"); PADDLE_ENFORCE_LE_UINT32_MAX(block_size_64, "fused_dropout_add block.x"); uint32_t grid_size = static_cast(grid_size_64); uint32_t block_size = static_cast(block_size_64); size_t offset = random_prop[2]; size_t main_offset = random_prop[3]; auto seed_tensor_ptr = seed_tensor.get_ptr(); funcs::GetSeedDataAndIncrement(dev_ctx, seed_tensor_ptr, fix_seed, seed, offset, &seed_data, &increment); seed_offset_data[0] = static_cast(seed_data); seed_offset_data[1] = static_cast(increment); auto dst_functor = NoMaskFwFunctor(1.0f - dropout_rate, upscale_in_train); // we assume seed/offset is same across iterations // seed_offset_data should preserved by cudaGraph pool const GPUContext* dev_ctx_p = &dev_ctx; // seed_offset_data should preserved by cudaGraph pool auto gen_cuda = dev_ctx.GetGenerator(); auto state_index = gen_cuda->GetStateIndex(); auto parameterSetter = [dev_ctx_p, offset, seed_offset_data, state_index, seed_tensor_ptr, fix_seed](backends::gpu::gpuKernelParams& params) { if (!fix_seed) { auto gen_cuda = dev_ctx_p->GetGenerator(); // ensure the generator use correct state index gen_cuda->SetStateIndex(state_index); // we assume seed is null pointer // seed copy to cpu is meaningless here assert(seed_tensor_ptr == nullptr); uint64_t seed, increment; std::tie(seed, increment) = gen_cuda->IncrementOffset(offset); VLOG(10) << "CUDA_GRAPH seed = " << seed << ", increment = " << increment; params.As(2) = seed; params.As(6) = increment; seed_offset_data[0] = static_cast(seed); seed_offset_data[1] = static_cast(increment); } }; backends::gpu::CUDAGraphNodeLauncher::gpuKernelCallback_t cudaKernelCallback = [=](unsigned int id) { void* functionPtr = reinterpret_cast( &(VectorizedDropoutForward>)); #ifdef PADDLE_WITH_HIP hipFunction_t cudaFunc = reinterpret_cast(functionPtr); #else cudaFunction_t cudaFunc; PADDLE_ENFORCE_GPU_SUCCESS( cudaGetFuncBySymbol(&cudaFunc, functionPtr)); #endif VLOG(10) << "[cudaKernelCallback] cudaFunc = " << cudaFunc << " functionPtr = " << functionPtr; VectorizedDropoutForward> <<>>(id, numel, seed_data, // need save x_data, y_data, out_data, increment, // need save main_offset, dst_functor); return cudaFunc; }; backends::gpu::CUDAGraphNodeLauncher::Instance().KernelNodeLaunch( parameterSetter, cudaKernelCallback); VLOG(10) << "NON_CUDA_GRAPH seed = " << seed_data << ", increment = " << increment; } else { using MT = typename phi::dtype::MPTypeTrait::Type; MT factor = static_cast(1.0f - dropout_rate); std::vector outs = {out}; std::vector ins = {&x, &y}; funcs::ElementwiseKernel( dev_ctx, ins, &outs, ScaleAddFuctor(factor, upscale_in_train)); } } } // namespace fusion } // namespace phi PD_REGISTER_KERNEL(fused_dropout_add, GPU, ALL_LAYOUT, phi::fusion::FusedDropoutAddKernel, float, double, phi::bfloat16, phi::float16) { kernel->OutputAt(1).SetDataType(phi::DataType::INT64); }