// Copyright (c) 2025 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. #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) #include #include "paddle/common/flags.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/core/visit_type.h" #include "paddle/phi/kernels/contiguous_kernel.h" #include "paddle/phi/kernels/full_kernel.h" #include "paddle/phi/kernels/funcs/dense_tensor_iterator.h" #include "paddle/phi/kernels/funcs/index_elementwise.cu.h" #include "paddle/phi/kernels/funcs/index_put_utils.h" #include "paddle/phi/kernels/funcs/indexing.h" #include "paddle/phi/kernels/funcs/stride_utils.h" #include "paddle/phi/kernels/funcs/strided_utils.h" #include "paddle/phi/kernels/index_put_grad_kernel.h" #include "paddle/phi/kernels/index_put_kernel.h" #include "paddle/phi/kernels/stride/elementwise_stride_base.cu.h" #if defined(__NVCC__) || defined(__HIPCC__) || defined(__xpu__) #include "paddle/phi/kernels/funcs/dims_simplifier.h" #endif COMMON_DECLARE_bool(use_stride_kernel); COMMON_DECLARE_bool(use_stride_compute_kernel); namespace phi { inline bool CheckIsDimsMatchBool(const DDim& first, const DDim& second) { int ignore_axis1 = 0, ignore_axis2 = 0; for (; ignore_axis1 < first.size(); ++ignore_axis1) { if (first[ignore_axis1] != 1) { break; } } for (; ignore_axis2 < second.size(); ++ignore_axis2) { if (second[ignore_axis2] != 1) { break; } } if (second.size() == ignore_axis2) { // second tensor has only one value return true; } if (first.size() - ignore_axis1 >= second.size() - ignore_axis2) { auto idx1 = first.size() - 1; auto idx2 = second.size() - 1; bool is_match = true; for (; idx2 >= ignore_axis2; idx2--) { if (first[idx1--] != second[idx2] && second[idx2] != 1) { is_match = false; break; } } if (is_match) { return true; } } return false; } template __device__ __forceinline__ void index_put_impl(char* out_data, const char* in_data, const char* const* index_ptrs, const int64_t* offsets, const int64_t* sizes, const int64_t* strides, bool accumulate) { int64_t offset = 0; #pragma unroll for (int64_t i = 0; i < num_indices; i++) { int64_t index = *reinterpret_cast(index_ptrs[i] + offsets[2]); if (index < 0) { index += sizes[i]; } offset += index * strides[i]; } if (accumulate) { *reinterpret_cast(out_data + offset) += *reinterpret_cast(in_data); } else { *reinterpret_cast(out_data + offset) = *reinterpret_cast(in_data); } } template void LaunchIndexPutKernel_V2(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.")); bool is_initialized = out->initialized(); auto meta = x.meta(); meta.dims = out->dims(); meta.strides = meta.calc_strides(out->dims()); out->set_meta(meta); T* out_data = dev_ctx.template Alloc(out); if (!is_initialized) { if (!x.meta().is_contiguous()) { StridedTensorCopy(x, vectorize(out->dims()), vectorize(out->strides()), 0, out); } else { phi::Copy(dev_ctx, x, dev_ctx.GetPlace(), false, out); } } funcs::AdvancedIndex ad = funcs::AdvancedIndex(dev_ctx, *out, indices); if (ad.empty_index) { if (!out->initialized()) { phi::Copy(dev_ctx, x, dev_ctx.GetPlace(), false, out); } return; } if (!CheckIsDimsMatchBool(ad.src.dims(), value.dims())) { DenseTensor x_; DenseTensor value_; if (!x.meta().is_contiguous()) { x_ = Tensor2Contiguous(dev_ctx, x); } else { x_ = x; } if (!value.meta().is_contiguous()) { value_ = Tensor2Contiguous(dev_ctx, value); } else { value_ = value; } phi::IndexPutKernel( dev_ctx, x_, indices, value_, accumulate, out); return; } int64_t numel = 0; int64_t num_indices = ad.indexed_sizes.size(); DenseTensorIteratorConfig config; config.add_output(ad.src); config.add_const_input(value); for (size_t i = 0; i < ad.indices.size(); i++) { config.add_const_input(*(ad.indices[i])); } DenseTensorIterator iter = config.build(); auto sizes = std::array{}; auto strides = std::array{}; auto index_ptrs = std::array{}; for (int64_t i = 0; i < num_indices; i++) { sizes[i] = ad.indexed_sizes[i]; strides[i] = ad.indexed_strides[i]; index_ptrs[i] = reinterpret_cast(iter.data_ptr(i + 2)); } bool is_big_tensor = false; int64_t max_stride = 0; for (int i = 0; i < 2 + num_indices; i++) { for (int j = 0; j < iter.ndim(); j++) { max_stride += iter.operands_[i].stride_bytes.data()[j] * iter.shape()[j]; } } if (!funcs::IsInUint32Range(max_stride * sizeof(T))) { is_big_tensor = true; } const int64_t N = iter.numel(); PADDLE_ENFORCE_EQ(true, (N >= 0 && N <= std::numeric_limits::max()), common::errors::PreconditionNotMet( "the value of N should be in [0, " "std::numeric_limits::max()]")); constexpr int nt = 128; constexpr int vt = 4; const dim3 block(nt); const dim3 grid((N + block.x * vt - 1) / (block.x * vt)); auto stream = dev_ctx.stream(); auto* val_data = value.data(); const char* in_ptr = reinterpret_cast(val_data); char* out_ptr = reinterpret_cast(out_data); #define Launch_Index_Put \ funcs::index_put_kernel<<>>( \ N, accumulate, [=] __device__(int64_t idx, bool accumulate) { \ const auto offsets = offset_calc.get(idx); \ char* const out_data = out_ptr + offsets[0]; \ const char* const in_data = in_ptr + offsets[1]; \ \ int64_t offset = 0; \ for (int64_t i = 0; i < num_indices; i++) { \ int64_t index = \ *reinterpret_cast(index_ptrs[i] + offsets[2]); \ if (index < 0) { \ index += sizes[i]; \ } \ offset += index * strides[i]; \ } \ if (accumulate) { \ *reinterpret_cast(out_data + offset) += \ *reinterpret_cast(in_data); \ } else { \ *reinterpret_cast(out_data + offset) = \ *reinterpret_cast(in_data); \ } \ }); if (is_big_tensor) { funcs::OffsetCalculator offset_calc = funcs::make_offset_calculator<3, false, uint64_t>(iter); Launch_Index_Put; } else { funcs::OffsetCalculator offset_calc = funcs::make_offset_calculator<3, false, uint32_t>(iter); Launch_Index_Put; } // funcs::OffsetCalculator offset_calc = // funcs::make_offset_calculator<3>(iter); } template void IndexPutKernel_V2(const Context& dev_ctx, const DenseTensor& x, const std::vector& indices, const DenseTensor& value, bool accumulate, DenseTensor* out) { if (!FLAGS_use_stride_kernel) { PADDLE_THROW(common::errors::Fatal( "FLAGS_use_stride_kernel is closed. Strided kernel " "be called, something wrong has happened!")); } DenseTensor x_; DenseTensor value_; for (size_t i = 0; i < indices.size(); i++) { PADDLE_ENFORCE_EQ(indices[i]->meta().is_contiguous(), true, common::errors::InvalidArgument( "Indices in Index_put must be contiguous.")); } bool zero_size = false; if (x.numel() == 0) { zero_size = true; } if (!FLAGS_use_stride_compute_kernel || zero_size) { if (!x.meta().is_contiguous()) { x_ = Tensor2Contiguous(dev_ctx, x); } else { x_ = x; } if (!value.meta().is_contiguous()) { value_ = Tensor2Contiguous(dev_ctx, value); } else { value_ = value; } auto meta = out->meta(); meta.strides = meta.calc_strides(out->dims()); out->set_meta(meta); phi::IndexPutKernel( dev_ctx, x_, indices, value_, accumulate, out); return; } x_ = x; value_ = value; if (!FLAGS_use_stride_compute_kernel) { PADDLE_THROW( common::errors::Fatal("FLAGS_use_stride_compute_kernel is closed. " "Kernel using DenseTensorIterator " "be called, something wrong has happened!")); } if (out && !funcs::IsInUint32Range(out->numel(), value_.numel())) { LaunchIndexPutKernel_V2( dev_ctx, x_, indices, value_, accumulate, out); } else { LaunchIndexPutKernel_V2( dev_ctx, x_, indices, value_, accumulate, out); } } template void IndexPutGradKernel_V2(const Context& dev_ctx, const DenseTensor& x, const std::vector& indices, const DenseTensor& value, const DenseTensor& out_grad, bool accumulate, DenseTensor* x_grad, DenseTensor* value_grad) { if (out_grad.numel() == 0) { dev_ctx.template Alloc(x_grad); // Fill value_grad with 0. if (value_grad) { phi::Full(dev_ctx, value_grad->dims(), 0, value_grad); } 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.")); DenseTensor out_grad_; if (!FLAGS_use_stride_compute_kernel || value_grad) { if (!out_grad.meta().is_contiguous()) { out_grad_ = Tensor2Contiguous(dev_ctx, out_grad); } else { out_grad_ = out_grad; } if (x_grad) { auto x_grad_meta = x.meta(); x_grad_meta.dims = x_grad->dims(); x_grad_meta.strides = x_grad_meta.calc_strides(x_grad->dims()); x_grad->set_meta(x_grad_meta); } if (value_grad) { auto value_grad_meta = value.meta(); value_grad_meta.dims = value_grad->dims(); value_grad_meta.strides = value_grad_meta.calc_strides(value_grad->dims()); value_grad->set_meta(value_grad_meta); } phi::IndexPutGradKernel( dev_ctx, x, indices, value, out_grad_, accumulate, x_grad, value_grad); return; } if (!FLAGS_use_stride_compute_kernel) { PADDLE_THROW( common::errors::Fatal("FLAGS_use_stride_compute_kernel is closed. " "Kernel using DenseTensorIterator " "be called, something wrong has happened!")); } if (x_grad) { if (accumulate) { auto meta = out_grad.meta(); x_grad->set_meta(meta); x_grad->ResetHolder(out_grad.Holder()); x_grad->ShareInplaceVersionCounterWith(out_grad); } else { DenseTensor value_zero; phi::Full(dev_ctx, value.dims(), 0, &value_zero); if (funcs::IsInUint32Range(x_grad->numel(), value.numel())) { LaunchIndexPutKernel_V2( dev_ctx, out_grad, indices, value_zero, false, x_grad); } else { LaunchIndexPutKernel_V2( dev_ctx, out_grad, indices, value_zero, false, x_grad); } } } } } // namespace phi PD_REGISTER_KERNEL(index_put, GPU, STRIDED, phi::IndexPutKernel_V2, float, double, int, int64_t, bool, int16_t, uint8_t, int8_t, phi::float16, phi::bfloat16, phi::complex64, phi::complex128) {} PD_REGISTER_KERNEL(index_put_grad, GPU, STRIDED, phi::IndexPutGradKernel_V2, float, double, int, int64_t, bool, int16_t, uint8_t, int8_t, phi::float16, phi::bfloat16, phi::complex64, phi::complex128) {} #endif