// 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/send_ue_recv_kernel.h" #include #include #include #include #include #include "paddle/common/enforce.h" #include "paddle/common/hostdevice.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/full_kernel.h" #include "paddle/phi/kernels/funcs/elementwise_functor.h" #include "paddle/phi/kernels/funcs/math_function.h" #include "paddle/phi/kernels/gpu/graph_send_recv_funcs.h" #include "paddle/phi/kernels/gpu/graph_send_ue_recv_funcs.h" #include "paddle/phi/kernels/impl/graph_message_passing_impl.h" namespace phi { template void GraphSendUERecvOpCUDAKernelLaunchHelper(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& e, const DenseTensor& src_index, const DenseTensor& dst_index, const std::string& message_op, const std::string& reduce_op, int64_t out_size, DenseTensor* out, DenseTensor* dst_count = nullptr) { // TODO(large-tensor): downstream functors may still use int; guard until // upgraded. const int64_t& index_size = src_index.dims()[0]; auto out_dims = out->dims(); int64_t memset_size = 1; std::vector dims_ = vectorize(out_dims); if (out_size <= 0) { dims_[0] = x.dims()[0]; } else { dims_[0] = out_size; } out->Resize(dims_); for (size_t i = 0; i < dims_.size(); i++) { memset_size *= dims_[i]; } // For float16/bfloat16 with reduce_op MIN/MAX, CudaAtomicMin/Max uses 4-byte // atomicCAS on 2-byte values. When the total element count is odd, the last // element's 4-byte CAS can read 2 bytes past the allocation boundary. // Request extra padding to avoid this out-of-bounds access. size_t requested_size = 0; if (sizeof(T) == 2 && (memset_size % 2 != 0) && (reduce_op == "MAX" || reduce_op == "MIN")) { requested_size = (memset_size + 1) * sizeof(T); } dev_ctx.template Alloc(out, requested_size); T* out_data = out->data(); const size_t& memset_bytes = memset_size * sizeof(T); funcs::SetConstant constant_functor; if (reduce_op == "SUM" || reduce_op == "MEAN") { constant_functor(dev_ctx, out, static_cast(0)); } else if (reduce_op == "MAX") { constant_functor(dev_ctx, out, std::numeric_limits::lowest()); } else if (reduce_op == "MIN") { constant_functor(dev_ctx, out, std::numeric_limits::max()); } if (index_size == 0) return; const auto& bcast_info = CalcBCastInfo(x.dims(), e.dims()); const T* x_data = x.data(); const T* e_data = e.data(); const IndexT* s_index = src_index.data(); const IndexT* d_index = dst_index.data(); thrust::device_vector x_bcastoff, e_bcastoff; if (bcast_info.use_bcast) { CopyBCastOff(bcast_info, &x_bcastoff, &e_bcastoff); } int64_t out_len = bcast_info.out_len; const int ntx = FindNumThreads(out_len, dev_ctx.GetMaxThreadsPerBlock()); const int nty = dev_ctx.GetMaxThreadsPerBlock() / ntx; const int64_t nbx_64 = (out_len + ntx - 1) / ntx; PADDLE_ENFORCE_LE_INT_MAX(nbx_64, "grid.x"); const int nbx = static_cast(nbx_64); const int64_t nby_64 = (index_size + nty - 1) / nty; const int nby = FindNumBlocks('y', nby_64); const dim3 grid(nbx, nby); const dim3 block(ntx, nty); int64_t input_size = x.dims()[0]; int block_ = 1024; if (reduce_op == "SUM" || reduce_op == "MEAN") { GraphSendUERecvSumCUDAFunctor sum_functor; if (message_op == "ADD") { funcs::AddFunctor add_funtor; GraphSendUERecvCUDAKernel, funcs::AddFunctor> <<>>( x_data, e_data, s_index, d_index, thrust::raw_pointer_cast(x_bcastoff.data()), thrust::raw_pointer_cast(e_bcastoff.data()), out_data, index_size, bcast_info.l_len, bcast_info.r_len, out_len, bcast_info.use_bcast, add_funtor, sum_functor); } else if (message_op == "MUL") { funcs::MultiplyFunctor mul_functor; GraphSendUERecvCUDAKernel, funcs::MultiplyFunctor> <<>>( x_data, e_data, s_index, d_index, thrust::raw_pointer_cast(x_bcastoff.data()), thrust::raw_pointer_cast(e_bcastoff.data()), out_data, index_size, bcast_info.l_len, bcast_info.r_len, out_len, bcast_info.use_bcast, mul_functor, sum_functor); } if (reduce_op == "MEAN") { input_size = out_size <= 0 ? x.dims()[0] : out_size; dst_count->Resize({input_size}); dev_ctx.template Alloc(dst_count); int* dst_count_data = dst_count->data(); #ifdef PADDLE_WITH_HIP hipMemset(dst_count_data, 0, input_size * sizeof(int)); #else cudaMemsetAsync( dst_count_data, 0, input_size * sizeof(int), dev_ctx.stream()); #endif int64_t grid_count = (index_size + block_ - 1) / block_; PADDLE_ENFORCE_LE_UINT32_MAX(grid_count, "grid_count"); ComputeCountCUDAKernel <<(grid_count), block_, 0, dev_ctx.stream()>>>( dst_count_data, d_index, index_size); int64_t grid_mean = (input_size * out_len + block_ - 1) / block_; int64_t max_grid_dimx = dev_ctx.GetCUDAMaxGridDimSize()[0]; int64_t grid_mean_ = grid_mean < max_grid_dimx ? grid_mean : max_grid_dimx; ManipulateMeanCUDAKernel<<>>( out_data, dst_count_data, input_size, out_len); } } else if (reduce_op == "MAX") { GraphSendUERecvMaxCUDAFunctor max_functor; if (message_op == "ADD") { funcs::AddFunctor add_funtor; GraphSendUERecvCUDAKernel, funcs::AddFunctor> <<>>( x_data, e_data, s_index, d_index, thrust::raw_pointer_cast(x_bcastoff.data()), thrust::raw_pointer_cast(e_bcastoff.data()), out_data, index_size, bcast_info.l_len, bcast_info.r_len, out_len, bcast_info.use_bcast, add_funtor, max_functor); } else if (message_op == "MUL") { funcs::MultiplyFunctor mul_functor; GraphSendUERecvCUDAKernel, funcs::MultiplyFunctor> <<>>( x_data, e_data, s_index, d_index, thrust::raw_pointer_cast(x_bcastoff.data()), thrust::raw_pointer_cast(e_bcastoff.data()), out_data, index_size, bcast_info.l_len, bcast_info.r_len, out_len, bcast_info.use_bcast, mul_functor, max_functor); } if (out_size > 0) { input_size = out_size; } int64_t grid_max = (input_size * out_len + block_ - 1) / block_; int64_t max_grid_dimx = dev_ctx.GetCUDAMaxGridDimSize()[0]; int64_t grid_max_ = grid_max < max_grid_dimx ? grid_max : max_grid_dimx; InputResetMaxCUDAKernel<<>>( out_data, input_size, out_len); } else if (reduce_op == "MIN") { GraphSendUERecvMinCUDAFunctor min_functor; if (message_op == "ADD") { funcs::AddFunctor add_funtor; GraphSendUERecvCUDAKernel, funcs::AddFunctor> <<>>( x_data, e_data, s_index, d_index, thrust::raw_pointer_cast(x_bcastoff.data()), thrust::raw_pointer_cast(e_bcastoff.data()), out_data, index_size, bcast_info.l_len, bcast_info.r_len, out_len, bcast_info.use_bcast, add_funtor, min_functor); } else if (message_op == "MUL") { funcs::MultiplyFunctor mul_functor; GraphSendUERecvCUDAKernel, funcs::MultiplyFunctor> <<>>( x_data, e_data, s_index, d_index, thrust::raw_pointer_cast(x_bcastoff.data()), thrust::raw_pointer_cast(e_bcastoff.data()), out_data, index_size, bcast_info.l_len, bcast_info.r_len, out_len, bcast_info.use_bcast, mul_functor, min_functor); } if (out_size > 0) { input_size = out_size; } int64_t grid_min = (input_size * out_len + block_ - 1) / block_; int64_t max_grid_dimx = dev_ctx.GetCUDAMaxGridDimSize()[0]; int64_t grid_min_ = grid_min < max_grid_dimx ? grid_min : max_grid_dimx; InputResetMinCUDAKernel<<>>( out_data, input_size, out_len); } } template void SendUERecvKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& y, const DenseTensor& src_index, const DenseTensor& dst_index, const std::string& message_op, const std::string& reduce_op, const IntArray& out_size, DenseTensor* out, DenseTensor* dst_count) { auto index_type = src_index.dtype(); auto& out_size_data = out_size.GetData(); if (x.numel() == 0 || y.numel() == 0 || src_index.numel() == 0 || dst_index.numel() == 0) { std::vector dims_ = vectorize(out->dims()); if (out_size_data[0] <= 0) { dims_[0] = x.dims()[0]; } else { dims_[0] = out_size_data[0]; } if (reduce_op == "MEAN") { int64_t input_size = out_size_data[0] <= 0 ? x.dims()[0] : out_size_data[0]; dst_count->Resize({input_size}); } out->Resize(dims_); Full(dev_ctx, out->dims(), 0, out); Full(dev_ctx, dst_count->dims(), 0, dst_count); return; } if (index_type == DataType::INT32) { GraphSendUERecvOpCUDAKernelLaunchHelper( dev_ctx, x, y, src_index, dst_index, message_op, reduce_op, out_size_data[0], out, dst_count); } else if (index_type == DataType::INT64) { GraphSendUERecvOpCUDAKernelLaunchHelper( dev_ctx, x, y, src_index, dst_index, message_op, reduce_op, out_size_data[0], out, dst_count); } } } // namespace phi PD_REGISTER_KERNEL(send_ue_recv, GPU, ALL_LAYOUT, phi::SendUERecvKernel, float, double, int, int64_t, phi::float16) { kernel->OutputAt(1).SetDataType(phi::DataType::INT32); }