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

463 lines
19 KiB
C++

// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
// Copyright 2022 The DGL team for some useful functions.
// 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
// thrust headers require nvcc/hipcc
// (rocThrust 7.0+ pulls in rocprim)
#if defined(__NVCC__) || defined(__HIPCC__)
#include <thrust/device_vector.h>
#include <thrust/fill.h>
#endif
#include "paddle/common/hostdevice.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/backends/gpu/gpu_primitives.h"
#include "paddle/phi/kernels/impl/graph_message_passing_impl.h"
namespace phi {
#define CUDA_MAX_NUM_BLOCKS_X 0x7FFFFFFF
#define CUDA_MAX_NUM_BLOCKS_Y 0xFFFF
#define CUDA_MAX_NUM_BLOCKS_Z 0xFFFF
inline void CopyBCastOff(const BroadCastInfo& bcast_info,
thrust::device_vector<int64_t>* l_bcastoff,
thrust::device_vector<int64_t>* r_bcastoff) {
l_bcastoff->resize(bcast_info.out_len);
r_bcastoff->resize(bcast_info.out_len);
#ifdef PADDLE_WITH_HIP
hipMemcpy(thrust::raw_pointer_cast(l_bcastoff->data()),
bcast_info.l_offset.data(),
sizeof(int64_t) * bcast_info.out_len,
hipMemcpyHostToDevice);
hipMemcpy(thrust::raw_pointer_cast(r_bcastoff->data()),
bcast_info.r_offset.data(),
sizeof(int64_t) * bcast_info.out_len,
hipMemcpyHostToDevice);
#else
cudaMemcpy(thrust::raw_pointer_cast(l_bcastoff->data()),
bcast_info.l_offset.data(),
sizeof(int64_t) * bcast_info.out_len,
cudaMemcpyHostToDevice);
cudaMemcpy(thrust::raw_pointer_cast(r_bcastoff->data()),
bcast_info.r_offset.data(),
sizeof(int64_t) * bcast_info.out_len,
cudaMemcpyHostToDevice);
#endif
}
inline int FindNumThreads(int64_t dim, int max_num_threads) {
PADDLE_ENFORCE_GE(dim,
0,
common::errors::PreconditionNotMet(
"Required dim >= 0, but received dim = %d", dim));
int res = max_num_threads;
if (dim == 0) res = 1;
while (res > dim) {
res = res >> 1;
}
res = res <= 32 ? 32 : res;
return res;
}
inline int FindNumBlocks(char axis, int64_t nblocks, int max_num_blocks = -1) {
int default_max_num_blocks = -1;
switch (axis) {
case 'x':
default_max_num_blocks = CUDA_MAX_NUM_BLOCKS_X;
break;
case 'y':
default_max_num_blocks = CUDA_MAX_NUM_BLOCKS_Y;
break;
case 'z':
default_max_num_blocks = CUDA_MAX_NUM_BLOCKS_Z;
break;
default:
PADDLE_THROW(
common::errors::InvalidArgument("%c axis is not recognized", axis));
}
if (max_num_blocks == -1) {
max_num_blocks = default_max_num_blocks;
}
PADDLE_ENFORCE_GE(nblocks,
0,
common::errors::InvalidArgument(
"The number of CUDA blocks must be non-negative. "
"Expected nblocks >= 0, but received nblocks = %ld.",
nblocks));
PADDLE_ENFORCE_GT(
max_num_blocks,
0,
common::errors::InvalidArgument("max_num_blocks should be larger than 0, "
"but received %d",
max_num_blocks));
if (nblocks < max_num_blocks) {
return static_cast<int>(nblocks);
}
return max_num_blocks;
}
template <typename T>
struct GraphSendUERecvSumCUDAFunctor {
DEVICE inline void operator()(T* output, T val) {
CudaAtomicAdd(output, val);
}
};
template <typename T>
struct GraphSendUERecvMaxCUDAFunctor {
DEVICE inline void operator()(T* output, T val) {
CudaAtomicMax(output, val);
}
};
template <typename T>
struct GraphSendUERecvMinCUDAFunctor {
DEVICE inline void operator()(T* output, T val) {
CudaAtomicMin(output, val);
}
};
template <typename T,
typename IndexT,
typename ReduceFunctor,
typename ComputeFunctor>
__global__ void GraphSendUERecvCUDAKernel(const T* x_data,
const T* e_data,
const IndexT* src_indices,
const IndexT* dst_indices,
const int64_t* xbcast_off,
const int64_t* ebcast_off,
T* output,
int64_t index_size,
int64_t x_len,
int64_t e_len,
int64_t out_len,
bool use_bcast,
ComputeFunctor cfunctor,
ReduceFunctor rfunctor) {
IndexT ty = static_cast<IndexT>(blockIdx.y) * blockDim.y + threadIdx.y;
const IndexT stride_y = static_cast<IndexT>(blockDim.y) * gridDim.y;
while (ty < index_size) {
IndexT src = src_indices[ty];
IndexT dst = dst_indices[ty];
int64_t tx = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
int64_t stride_x = blockDim.x * static_cast<int64_t>(gridDim.x);
const T* x_off = x_data + src * x_len;
const T* e_off = e_data + ty * e_len;
T* out_off = output + dst * out_len;
while (tx < out_len) {
int64_t x_add = use_bcast ? xbcast_off[tx] : tx;
int64_t e_add = use_bcast ? ebcast_off[tx] : tx;
T val = cfunctor(x_off[x_add], e_off[e_add]);
rfunctor(out_off + tx, val);
tx += stride_x;
}
ty += stride_y;
}
}
// x_grad: for backward mean with mul.
template <typename T, typename IndexT>
__global__ void ManipulateMeanGradCUDAKernelForMulX(const T* out_grad_data,
const T* e_data,
const IndexT* src_indices,
const IndexT* dst_indices,
const int* dst_count,
const int64_t* l_bcastoff,
const int64_t* r_bcastoff,
T* x_grad,
int64_t index_size,
int64_t l_len,
int64_t r_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT src = src_indices[ty];
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
const T* out_grad_off = out_grad_data + src * l_len;
const T* e_off = e_data + ty * r_len;
T* x_grad_off = x_grad + dst * out_len;
while (tx < out_len) {
int64_t o_add = use_bcast ? l_bcastoff[tx] : tx;
int64_t e_add = use_bcast ? r_bcastoff[tx] : tx;
T val = out_grad_off[o_add] * e_off[e_add];
CudaAtomicAdd(x_grad_off + tx, val / static_cast<T>(dst_count[src]));
tx += stride_x;
}
ty += stride_y;
}
}
// e_grad: backward sum for add.
template <typename T, typename IndexT>
__global__ void ManipulateSumGradCUDAKernelForAddE(const T* out_grad_data,
const IndexT* dst_indices,
const int64_t* r_bcastoff,
T* e_grad,
int64_t index_size,
int64_t r_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
T* e_grad_off = e_grad + ty * r_len;
const T* out_grad_off = out_grad_data + dst * out_len;
while (tx < out_len) {
int64_t e_add = use_bcast ? r_bcastoff[tx] : tx;
CudaAtomicAdd(e_grad_off + e_add, out_grad_off[tx]);
tx += stride_x;
}
ty += stride_y;
}
}
// e_grad: backward sum for mul.
template <typename T, typename IndexT>
__global__ void ManipulateSumGradCUDAKernelForMulE(const T* x_data,
const T* out_grad_data,
const IndexT* src_indices,
const IndexT* dst_indices,
const int64_t* l_bcastoff,
const int64_t* r_bcastoff,
T* e_grad,
int64_t index_size,
int64_t l_len,
int64_t r_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT src = src_indices[ty];
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
const T* x_off = x_data + src * l_len;
T* e_grad_off = e_grad + ty * r_len;
const T* out_grad_off = out_grad_data + dst * out_len;
while (tx < out_len) {
int64_t x_add = use_bcast ? l_bcastoff[tx] : tx;
int64_t e_add = use_bcast ? r_bcastoff[tx] : tx;
CudaAtomicAdd(e_grad_off + e_add, out_grad_off[tx] * x_off[x_add]);
tx += stride_x;
}
ty += stride_y;
}
}
// e_grad: backward mean for add
template <typename T, typename IndexT>
__global__ void ManipulateMeanGradCUDAKernelForAddE(const T* out_grad_data,
const IndexT* dst_indices,
const int* dst_count,
const int64_t* r_bcastoff,
T* e_grad,
int64_t index_size,
int64_t r_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
T* e_grad_off = e_grad + ty * r_len;
const T* out_grad_off = out_grad_data + dst * out_len;
while (tx < out_len) {
int64_t e_add = use_bcast ? r_bcastoff[tx] : tx;
CudaAtomicAdd(e_grad_off + e_add,
out_grad_off[tx] / static_cast<T>(dst_count[dst]));
tx += stride_x;
}
ty += stride_y;
}
}
// e_grad: backward mean for mul.
template <typename T, typename IndexT>
__global__ void ManipulateMeanGradCUDAKernelForMulE(const T* x_data,
const T* out_grad_data,
const IndexT* src_indices,
const IndexT* dst_indices,
const int* dst_count,
const int64_t* l_bcastoff,
const int64_t* r_bcastoff,
T* e_grad,
int64_t index_size,
int64_t l_len,
int64_t r_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT src = src_indices[ty];
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
const T* x_off = x_data + src * l_len;
T* e_grad_off = e_grad + ty * r_len;
const T* out_grad_off = out_grad_data + dst * out_len;
while (tx < out_len) {
int64_t x_add = use_bcast ? l_bcastoff[tx] : tx;
int64_t e_add = use_bcast ? r_bcastoff[tx] : tx;
CudaAtomicAdd(
e_grad_off + e_add,
out_grad_off[tx] * x_off[x_add] / static_cast<T>(dst_count[dst]));
tx += stride_x;
}
ty += stride_y;
}
}
// x_grad, e_grad: backward min and max for add.
template <typename T, typename IndexT>
__global__ void ManipulateMinMaxGradCUDAKernelForAdd(const T* x_data,
const T* e_data,
const T* out,
const T* out_grad,
const IndexT* src_indices,
const IndexT* dst_indices,
const int64_t* xbcast_off,
const int64_t* ebcast_off,
T* x_grad,
T* e_grad,
int64_t index_size,
int64_t x_len,
int64_t e_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT src = src_indices[ty];
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
const T* x_off = x_data + dst * x_len;
const T* e_off = e_data + ty * e_len;
const T* out_off = out + src * out_len;
const T* out_grad_off = out_grad + src * out_len;
T* x_grad_off = x_grad + dst * x_len;
T* e_grad_off = e_grad + ty * e_len;
while (tx < out_len) {
int64_t x_add = use_bcast ? xbcast_off[tx] : tx;
int64_t e_add = use_bcast ? ebcast_off[tx] : tx;
T val = x_off[x_add] + e_off[e_add];
CudaAtomicAdd(x_grad_off + x_add,
out_grad_off[tx] * static_cast<T>(val == out_off[tx]));
CudaAtomicAdd(e_grad_off + e_add,
out_grad_off[tx] * static_cast<T>(val == out_off[tx]));
tx += stride_x;
}
ty += stride_y;
}
}
// x_grad, e_grad: backward min and max for mul.
template <typename T, typename IndexT>
__global__ void ManipulateMinMaxGradCUDAKernelForMul(const T* x_data,
const T* e_data,
const T* out,
const T* out_grad,
const IndexT* src_indices,
const IndexT* dst_indices,
const int64_t* xbcast_off,
const int64_t* ebcast_off,
T* x_grad,
T* e_grad,
int64_t index_size,
int64_t x_len,
int64_t e_len,
int64_t out_len,
bool use_bcast) {
IndexT ty = blockIdx.y * blockDim.y + threadIdx.y;
const IndexT stride_y = blockDim.y * gridDim.y;
while (ty < index_size) {
IndexT src = src_indices[ty];
IndexT dst = dst_indices[ty];
int64_t tx =
static_cast<int64_t>(blockIdx.x) * static_cast<int64_t>(blockDim.x) +
static_cast<int64_t>(threadIdx.x);
int64_t stride_x =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(gridDim.x);
const T* x_off = x_data + dst * x_len;
const T* e_off = e_data + ty * e_len;
const T* out_off = out + src * out_len;
const T* out_grad_off = out_grad + src * out_len;
T* x_grad_off = x_grad + dst * x_len;
T* e_grad_off = e_grad + ty * e_len;
while (tx < out_len) {
int64_t x_add = use_bcast ? xbcast_off[tx] : tx;
int64_t e_add = use_bcast ? ebcast_off[tx] : tx;
T val = x_off[x_add] * e_off[e_add];
CudaAtomicAdd(
x_grad_off + x_add,
out_grad_off[tx] * static_cast<T>(val == out_off[tx]) * e_off[e_add]);
CudaAtomicAdd(
e_grad_off + e_add,
out_grad_off[tx] * static_cast<T>(val == out_off[tx]) * x_off[x_add]);
tx += stride_x;
}
ty += stride_y;
}
}
} // namespace phi