// 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/phi/kernels/index_put_kernel.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/backends/gpu/gpu_launch_config.h" #include "paddle/phi/backends/gpu/gpu_primitives.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/cast_kernel.h" #include "paddle/phi/kernels/funcs/index_put_utils.h" namespace phi { template __global__ void IndexPutCudaKernel(const T* x, const T* vals, int64_t** indices, Array stride, Array shape, const int rank, const int64_t numel, const int64_t is_single_val_tensor, const bool accumulate, T* out) { int64_t idx = static_cast(threadIdx.x) + static_cast(blockDim.x) * static_cast(blockIdx.x); int64_t cur_ix = 0; if (idx >= numel) { return; } int64_t offset = 0; #pragma unroll for (int i = 0; i < DDim::kMaxRank; ++i) { if (i >= rank) { break; } cur_ix = (static_cast(*(indices[i] + idx))); if (cur_ix < 0) { cur_ix += shape[i]; } offset += stride[i] * cur_ix; } if (accumulate) { CudaAtomicAdd(out + offset, *(vals + (idx & is_single_val_tensor))); } else { *(out + offset) = *(vals + (idx & is_single_val_tensor)); } } template void LaunchIndexPutCudaKernel(const Context& dev_ctx, const DenseTensor& x, const std::vector& indices, const DenseTensor& value, bool accumulate, DenseTensor* out) { auto* x_data = x.data(); auto* val_data = value.data(); bool is_initialized = out->initialized(); T* out_data = dev_ctx.template Alloc(out); if (!is_initialized) { Copy(dev_ctx, x, dev_ctx.GetPlace(), false, out); } auto x_dims = x.dims(); const int rank = x_dims.size(); auto x_stride = common::stride(x_dims); Array stride_array; Array shape_array; for (int i = 0; i < rank; ++i) { stride_array[i] = x_stride[i]; shape_array[i] = x_dims[i]; } int64_t is_single_val_tensor = (value.numel() == 1) ? 0 : INT64_MAX; const int64_t numel = indices[0]->numel(); Allocator::AllocationPtr holder; auto pd_indices = funcs::GetDevicePointerArray(dev_ctx, indices, &holder); auto config = backends::gpu::GetGpuLaunchConfig1D(dev_ctx, numel); IndexPutCudaKernel <<>>( x_data, val_data, pd_indices, stride_array, shape_array, rank, numel, is_single_val_tensor, accumulate, out_data); } template void IndexPutKernel(const Context& dev_ctx, const DenseTensor& x, const std::vector& indices, const DenseTensor& value, bool accumulate, DenseTensor* out) { if (out && out->numel() == 0) { dev_ctx.template Alloc(out); return; } PADDLE_ENFORCE_EQ( x.dtype(), value.dtype(), common::errors::InvalidArgument( "The data type of tensor value must be same to the data type " "of tensor x.")); PADDLE_ENFORCE_EQ( indices.empty(), false, common::errors::InvalidArgument("Indices cannot be empty.")); std::vector tmp_args; std::vector int_indices_v = funcs::DealWithBoolIndices(dev_ctx, indices, &tmp_args); if (int_indices_v.empty()) { if (!out->initialized()) { Copy(dev_ctx, x, dev_ctx.GetPlace(), false, out); } return; } auto bd_dim = funcs::BroadCastTensorsDims(int_indices_v); std::vector res_dim_v(vectorize(bd_dim)); std::vector res_indices_v(x.dims().size(), nullptr); std::vector tmp_res_indices_v; std::vector tmp_value_v; std::vector range_tensor_v; const DenseTensor* ptr_value = nullptr; for (int i = int_indices_v.size(); i < x.dims().size(); ++i) { range_tensor_v.emplace_back(funcs::GetRangeCudaTensor( dev_ctx, x.dims()[i], DataType::INT64)); } funcs::DealWithIndices(dev_ctx, x, int_indices_v, &res_indices_v, &tmp_res_indices_v, range_tensor_v, bd_dim, &res_dim_v); if (value.numel() != 1) { tmp_value_v.emplace_back(DenseTensor(value.dtype()).Resize(res_dim_v)); ExpandKernel( dev_ctx, value, IntArray(res_dim_v), &tmp_value_v[0]); ptr_value = &tmp_value_v[0]; } else { ptr_value = &value; } LaunchIndexPutCudaKernel( dev_ctx, x, res_indices_v, *ptr_value, accumulate, out); } } // namespace phi PD_REGISTER_KERNEL(index_put, GPU, ALL_LAYOUT, phi::IndexPutKernel, float, double, int, int64_t, bool, int16_t, uint8_t, int8_t, phi::float16, phi::bfloat16, phi::complex64, phi::complex128) {}