Files
paddlepaddle--paddle/paddle/phi/kernels/funcs/index_put_utils.h
T
2026-07-13 12:40:42 +08:00

366 lines
12 KiB
C++

// 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.
#pragma once
#include <vector>
#include "paddle/common/array.h"
#include "paddle/phi/backends/context_pool.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/kernels/cast_kernel.h"
#include "paddle/phi/kernels/expand_kernel.h"
#include "paddle/phi/kernels/nonzero_kernel.h"
#include "paddle/phi/kernels/reshape_kernel.h"
#include "paddle/phi/kernels/split_kernel.h"
#if defined(__NVCC__) || defined(__HIPCC__)
#ifdef __NVCC__
#include <cuda.h>
#include <cuda_runtime.h>
#elif defined(__HIPCC__)
#include <hip/hip_runtime.h>
#endif
#endif
#ifdef PADDLE_WITH_CUDA
#include "paddle/phi/backends/gpu/cuda/cuda_graph_with_memory_pool.h"
#endif
namespace phi {
namespace funcs {
template <typename T, typename Context>
DenseTensor GetReshapeAndExpandTensor(const Context& dev_ctx,
const DenseTensor& tensor,
const DDim& res_dim,
const DDim& bd_dim,
int index) {
std::vector<int64_t> before_dims = vectorize(tensor.dims());
std::vector<int64_t> mid_dims(res_dim.size(), 1);
if (index == 0) {
for (size_t i = 0; i < before_dims.size(); ++i) {
mid_dims[bd_dim.size() - i - 1] = before_dims[before_dims.size() - i - 1];
}
} else {
mid_dims[index] = before_dims[0];
}
DenseTensor mid_tensor(tensor.dtype());
mid_tensor.Resize(mid_dims);
ReshapeKernel<Context>(dev_ctx, tensor, IntArray(mid_dims), &mid_tensor);
DenseTensor res_tensor(tensor.dtype());
res_tensor.Resize(res_dim);
ExpandKernel<T, Context>(
dev_ctx, mid_tensor, IntArray(vectorize(res_dim)), &res_tensor);
return res_tensor;
}
template <typename T, typename Context>
std::vector<const DenseTensor*> DealWithBoolIndices(
const Context& dev_ctx,
const std::vector<const DenseTensor*>& indices_v,
std::vector<DenseTensor>* tmp_indices_v) {
std::vector<const DenseTensor*> res;
bool contains_bool_tensor = false;
for (size_t i = 0; i < indices_v.size(); ++i) {
if (indices_v[i]->dtype() == DataType::BOOL) {
contains_bool_tensor = true;
break;
}
}
if (contains_bool_tensor) {
for (size_t i = 0; i < indices_v.size(); ++i) {
if (indices_v[i]->dtype() == DataType::BOOL) {
int rank = indices_v[i]->dims().size();
PADDLE_ENFORCE_GE(rank,
1UL,
common::errors::InvalidArgument(
"the only bool tensor in indices should "
"have number of dimension at least 1"));
DenseTensor nonzero_indices(DataType::INT64);
nonzero_indices.Resize({-1, rank});
NonZeroKernel<bool, Context>(dev_ctx, *indices_v[i], &nonzero_indices);
if (nonzero_indices.numel() == 0) {
std::vector<const DenseTensor*> empty_indices;
return empty_indices;
}
std::vector<DenseTensor*> integer_indices(rank, nullptr);
const int tmp_ix = tmp_indices_v->size();
for (int i = 0; i < rank; ++i) {
tmp_indices_v->emplace_back(
DenseTensor(DataType::INT64).Resize({nonzero_indices.dims()[0]}));
}
for (int i = 0; i < rank; ++i) {
integer_indices[i] = &((*tmp_indices_v)[i + tmp_ix]);
}
SplitWithNumKernel<int64_t, Context>(
dev_ctx, nonzero_indices, rank, 1, integer_indices);
#ifdef PADDLE_WITH_XPU
auto place = dev_ctx.GetPlace();
if (place.GetType() == AllocationType::XPU) {
auto& pool = DeviceContextPool::Instance();
auto* xpu_ctx = static_cast<XPUContext*>(pool.Get(place));
if (xpu_ctx->x_context()->xpu_stream) {
dev_ctx.Wait();
}
}
#endif
} else if ((indices_v[i]->dtype() == DataType::INT64) ||
(indices_v[i]->dtype() == DataType::INT32)) {
tmp_indices_v->emplace_back(*indices_v[i]);
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"data type of tensor in indices must be int32, int64 or bool"));
}
}
res.reserve(tmp_indices_v->size());
for (size_t i = 0; i < tmp_indices_v->size(); ++i) {
res.emplace_back(&((*tmp_indices_v)[i]));
}
} else {
res = indices_v;
}
return res;
}
static DDim BroadCastTensorsDims(
const std::vector<const DenseTensor*>& tensors) {
int target_rank = 0;
for (const auto& tensor : tensors) {
target_rank = std::max(target_rank, tensor->dims().size());
}
PADDLE_ENFORCE_GT(target_rank,
0,
errors::InvalidArgument("BroadCastTensorsDims requires at "
"least one input tensor to have "
"rank greater than zero"));
std::vector<int64_t> target_dims(target_rank, 0);
for (int index = 0; index < target_rank; index++) {
int64_t target_dim_size = 1;
for (const auto& tensor : tensors) {
auto input_ddim = tensor->dims();
int axis = static_cast<int>(input_ddim.size()) - index - 1;
int64_t dim_size = 1;
if (axis >= 0) {
dim_size = input_ddim[axis];
}
if (target_dim_size != 1 && dim_size != 1 &&
target_dim_size != dim_size) {
PADDLE_THROW(errors::InvalidArgument(
"BroadCastTensorsDims inputs does not satisfy bcast semantics, "
"please check axis = %d in reverse order",
index));
}
target_dim_size = dim_size == 1 ? target_dim_size : dim_size;
}
target_dims[target_rank - index - 1] = target_dim_size;
}
return make_ddim(target_dims);
}
template <typename T, typename Context>
T** GetDevicePointerArray(const Context& dev_ctx,
const std::vector<const DenseTensor*>& indices_v,
phi::Allocator::AllocationPtr* holder_ptr) {
PADDLE_ENFORCE_NOT_NULL(
holder_ptr,
common::errors::InvalidArgument(
"hold_ptr should be provided when calling GetDevicePointerArray."));
std::vector<const T*> h_indices_v(indices_v.size());
for (size_t i = 0; i < indices_v.size(); ++i) {
h_indices_v[i] = indices_v[i]->data<T>();
}
auto& d_indices_data = *holder_ptr;
d_indices_data = phi::memory_utils::Alloc(
dev_ctx.GetPlace(),
h_indices_v.size() * sizeof(T*),
phi::Stream(reinterpret_cast<phi::StreamId>(dev_ctx.stream())));
size_t nbytes_idx = h_indices_v.size() * sizeof(T*);
#ifdef PADDLE_WITH_CUDA
const void* stable_idx =
phi::backends::gpu::RestoreHostMemIfCapturingCUDAGraph(
reinterpret_cast<uint8_t*>(h_indices_v.data()), nbytes_idx);
#else
const void* stable_idx = reinterpret_cast<const void*>(h_indices_v.data());
#endif
phi::memory_utils::Copy(dev_ctx.GetPlace(),
d_indices_data->ptr(),
CPUPlace(),
stable_idx,
nbytes_idx,
dev_ctx.stream());
return reinterpret_cast<T**>(d_indices_data->ptr());
}
template <typename T, typename Context>
void DealWithIndices(const Context& dev_ctx,
const DenseTensor& x,
const std::vector<const DenseTensor*>& int_indices_v,
std::vector<const DenseTensor*>* res_indices_v,
std::vector<DenseTensor>* tmp_res_indices_v,
const std::vector<DenseTensor>& range_tensor_v,
const DDim& bd_dim,
std::vector<int64_t>* res_dim_v) {
size_t total_dims = x.dims().size();
if (int_indices_v.size() < total_dims) {
std::vector<int64_t> tmp_x_dims = vectorize(x.dims());
int len_bd_dim = bd_dim.size();
res_dim_v->insert(res_dim_v->end(),
tmp_x_dims.begin() + int_indices_v.size(),
tmp_x_dims.end());
DDim res_dim = make_ddim(*res_dim_v);
for (size_t i = 0; i < int_indices_v.size(); ++i) {
DenseTensor index_tensor;
if (int_indices_v[i]->dtype() == DataType::INT32) {
index_tensor =
Cast<int, Context>(dev_ctx, *int_indices_v[i], DataType::INT64);
} else {
index_tensor = *int_indices_v[i];
}
tmp_res_indices_v->emplace_back(
GetReshapeAndExpandTensor<int64_t, Context>(
dev_ctx, index_tensor, res_dim, bd_dim, 0));
}
for (size_t i = 0; i < range_tensor_v.size(); ++i) {
tmp_res_indices_v->emplace_back(
GetReshapeAndExpandTensor<int64_t, Context>(
dev_ctx, range_tensor_v[i], res_dim, bd_dim, i + len_bd_dim));
}
for (size_t i = 0; i < res_indices_v->size(); ++i) {
(*res_indices_v)[i] = &(*tmp_res_indices_v)[i];
}
} else {
for (size_t i = 0; i < int_indices_v.size(); ++i) {
DenseTensor index_tensor;
DenseTensor expand_index;
if (int_indices_v[i]->dtype() == DataType::INT32) {
index_tensor =
Cast<int, Context>(dev_ctx, *int_indices_v[i], DataType::INT64);
} else {
index_tensor = *int_indices_v[i];
}
if (bd_dim != int_indices_v[i]->dims()) {
expand_index = DenseTensor(DataType::INT64).Resize(bd_dim);
ExpandKernel<int64_t, Context>(dev_ctx,
index_tensor,
IntArray(vectorize<int64_t>(bd_dim)),
&expand_index);
} else {
expand_index = index_tensor;
}
tmp_res_indices_v->emplace_back(expand_index);
}
for (size_t i = 0; i < res_indices_v->size(); ++i) {
(*res_indices_v)[i] = &(*tmp_res_indices_v)[i];
}
}
}
static void CalCompressedDimsWith1AndWithout1(
std::vector<int64_t>* after_dims,
std::vector<int64_t>* before_dims,
std::vector<int64_t>* compress_dims,
std::vector<int64_t>* dims_without_1) {
int i = static_cast<int>(after_dims->size()) - 1;
int j = static_cast<int>(before_dims->size()) - 1;
if (i < j) {
PADDLE_THROW(common::errors::InvalidArgument(
"shape of value can't not be broadcast to shape of x[indices]"));
}
while ((i >= 0) && (j >= 0)) {
if ((*after_dims)[i] == (*before_dims)[j]) {
dims_without_1->push_back((*before_dims)[j]);
i--;
j--;
continue;
} else if ((*before_dims)[j] == 1) {
compress_dims->push_back(i);
i--;
j--;
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"shape of value can't not be broadcast to shape of x[indices]"));
}
}
while (i >= 0) {
compress_dims->push_back(i);
i--;
}
}
#if defined(__NVCC__) || defined(__HIPCC__)
template <typename T>
__global__ void range_cuda_kernel(int64_t N, T* out) {
int64_t idx = threadIdx.x + static_cast<int64_t>(blockDim.x) * blockIdx.x;
if (idx >= N) {
return;
}
out[idx] = idx;
}
template <typename T, typename Context>
DenseTensor GetRangeCudaTensor(const Context& dev_ctx,
int64_t N,
DataType dtype) {
DenseTensor res(dtype);
res.Resize({N});
DenseTensor* p_res = &res;
T* out = dev_ctx.template Alloc<T>(p_res);
auto config = phi::backends::gpu::GetGpuLaunchConfig1D(dev_ctx, N);
range_cuda_kernel<T>
<<<config.block_per_grid, config.thread_per_block, 0, dev_ctx.stream()>>>(
N, out);
return res;
}
#endif
template <typename T>
void range_kernel(int64_t N, T* out) {
for (int64_t idx = 0; idx < N; ++idx) {
out[idx] = idx;
}
}
template <typename T, typename Context>
DenseTensor GetRangeTensor(const Context& dev_ctx, int64_t N, DataType dtype) {
DenseTensor res(dtype);
res.Resize({N});
DenseTensor* p_res = &res;
T* out = dev_ctx.template Alloc<T>(p_res);
range_kernel<T>(N, out);
return res;
}
} // namespace funcs
} // namespace phi