830 lines
27 KiB
C++
830 lines
27 KiB
C++
// 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.
|
|
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
#include "paddle/common/array.h"
|
|
#include "paddle/phi/backends/context_pool.h"
|
|
#include "paddle/phi/backends/gpu/gpu_context.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/elementwise_add_kernel.h"
|
|
#include "paddle/phi/kernels/elementwise_kernel.h"
|
|
#include "paddle/phi/kernels/elementwise_multiply_kernel.h"
|
|
#include "paddle/phi/kernels/expand_kernel.h"
|
|
#include "paddle/phi/kernels/full_kernel.h"
|
|
#include "paddle/phi/kernels/funcs/indexing.h"
|
|
#include "paddle/phi/kernels/nonzero_kernel.h"
|
|
#include "paddle/phi/kernels/slice_kernel.h"
|
|
#include "paddle/phi/kernels/transpose_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 {
|
|
|
|
static inline std::vector<int64_t> infer_size_dimvector(
|
|
const std::vector<int64_t>& a, const std::vector<int64_t>& b) {
|
|
// Use ptrdiff_t to ensure signed comparison.
|
|
auto dimsA = a.size();
|
|
auto dimsB = b.size();
|
|
auto ndim = dimsA > dimsB ? dimsA : dimsB;
|
|
std::vector<int64_t> expandedSizes = std::vector<int64_t>(ndim, 0);
|
|
|
|
for (int64_t i = ndim - 1; i >= 0; --i) {
|
|
int64_t offset = ndim - 1 - i;
|
|
int64_t dimA = dimsA - 1 - offset;
|
|
int64_t dimB = dimsB - 1 - offset;
|
|
auto sizeA = (dimA >= 0) ? a[dimA] : 1;
|
|
auto sizeB = (dimB >= 0) ? b[dimB] : 1;
|
|
|
|
expandedSizes[i] = sizeA == 1 ? sizeB : sizeA;
|
|
}
|
|
|
|
return expandedSizes;
|
|
}
|
|
|
|
static inline std::vector<int64_t> compute_strides(
|
|
const std::vector<int64_t>& input_dims, // value_tensor
|
|
const std::vector<int64_t>& input_strides,
|
|
const int64_t& input_elesize,
|
|
const int64_t& ndim,
|
|
const std::vector<int64_t>* shape_,
|
|
std::vector<int64_t>* stride_size) {
|
|
std::vector<int64_t> stride_bytes(ndim, 0);
|
|
const auto& original_shape = input_dims;
|
|
const auto& original_stride = input_strides;
|
|
int64_t element_size_in_bytes = input_elesize;
|
|
int offset = ndim - original_shape.size();
|
|
if (offset > 0)
|
|
stride_bytes.resize(ndim, 0);
|
|
else
|
|
stride_bytes.resize(ndim);
|
|
for (size_t i = 0; i < original_shape.size(); i++) {
|
|
if (original_shape[i] == 1 && (*shape_)[offset + i] != 1) {
|
|
stride_bytes[offset + i] = 0;
|
|
} else {
|
|
stride_bytes[offset + i] = original_stride[i] * element_size_in_bytes;
|
|
}
|
|
}
|
|
stride_size->push_back(stride_bytes.size());
|
|
return stride_bytes;
|
|
}
|
|
|
|
static inline std::vector<int64_t> compute_shapes(
|
|
const std::vector<std::vector<int64_t>>& input_dims) {
|
|
std::vector<int64_t> shape_;
|
|
for (size_t i = 0; i < input_dims.size(); i++) {
|
|
auto shape = input_dims[i];
|
|
if (shape_.empty()) {
|
|
shape_ = shape;
|
|
} else if (!(shape == shape_)) {
|
|
shape_ = infer_size_dimvector(shape_, shape);
|
|
}
|
|
}
|
|
return shape_;
|
|
}
|
|
|
|
template <int N>
|
|
static inline void permute_dimensions(const std::vector<int64_t>& stride_size,
|
|
const std::vector<int64_t>& perm,
|
|
std::array<int64_t*, N>* strides_array,
|
|
std::vector<int64_t>* shape_) {
|
|
auto reorder = [perm](std::vector<int64_t> data) {
|
|
auto res = std::vector<int64_t>(data.size(), 0);
|
|
for (size_t i = 0; i < perm.size(); i++) {
|
|
res[i] = data[perm[i]];
|
|
}
|
|
return res;
|
|
};
|
|
|
|
// Update shape and strides
|
|
*shape_ = reorder(*shape_);
|
|
std::array<std::vector<int64_t>, N> temp_strides;
|
|
for (int64_t i = 0; i < N; i++) {
|
|
if ((*strides_array)[i] != nullptr) {
|
|
std::vector<int64_t> original_data((*strides_array)[i],
|
|
(*strides_array)[i] + stride_size[i]);
|
|
temp_strides[i] = reorder(original_data);
|
|
for (int64_t j = 0; j < stride_size[i]; j++) {
|
|
(*strides_array)[i][j] = temp_strides[i][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <int N>
|
|
static inline void reorder_dimensions(const std::vector<int64_t>& stride_size,
|
|
std::vector<int64_t>* shape_,
|
|
std::array<int64_t*, N>* strides_array) {
|
|
// Sort the dimensions based on strides in ascending order with reduced dims
|
|
// at the front. NOTE: that this inverts the order of C-contiguous tensors.
|
|
// strides[0] is the fastest moving dimension instead of strides[ndim - 1].
|
|
// See NOTE: [Computing output strides] and inline comments for more detailed
|
|
// description
|
|
auto ndim = shape_->size();
|
|
std::vector<int64_t> perm_;
|
|
|
|
perm_.resize(ndim);
|
|
if (ndim == 1) {
|
|
perm_[0] = 0;
|
|
return;
|
|
}
|
|
|
|
// initialize perm with n-1, n-2, ..., 1, 0
|
|
std::iota(perm_.rbegin(), perm_.rend(), 0);
|
|
// returns 1 if the dim0 should come after dim1, -1 if dim0 should come
|
|
// before dim1, and 0 if the comparison is ambiguous.
|
|
auto should_swap = [&](size_t dim0, size_t dim1) {
|
|
for (int64_t arg = 0; arg < N; arg++) {
|
|
// ignore undefined or incorrectly sized tensors
|
|
if ((*strides_array)[arg] == nullptr) {
|
|
continue;
|
|
}
|
|
int64_t stride0 = (*strides_array)[arg][dim0];
|
|
int64_t stride1 = (*strides_array)[arg][dim1];
|
|
// move on to the next input if one of the dimensions is broadcasted
|
|
if (stride0 == 0 || stride1 == 0) {
|
|
continue;
|
|
// it is important to return here only with strict comparisons, for
|
|
// equal strides we try to break the tie later by comparing
|
|
// corresponding dimensions or if that does not work, moving on to the
|
|
// next tensor
|
|
} else if (stride0 < stride1) {
|
|
return -1;
|
|
} else if (stride0 > stride1) {
|
|
return 1;
|
|
} else { // equal strides, use dimensions themselves as the tie-breaker.
|
|
// at this point, with zero strides out of the way, we are guaranteed
|
|
// that operand dimensions are equal to shape_
|
|
auto t_dim0 = (*shape_)[dim0];
|
|
auto t_dim1 = (*shape_)[dim1];
|
|
// return only if dimensions should be swapped, otherwise move on to the
|
|
// next tensor
|
|
if (t_dim0 > t_dim1) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
};
|
|
// insertion sort with support for ambiguous comparisons
|
|
for (size_t i = 1; i < ndim; i++) {
|
|
int dim1 = i;
|
|
for (int dim0 = i - 1; dim0 >= 0; dim0--) {
|
|
int comparison = should_swap(perm_[dim0], perm_[dim1]);
|
|
if (comparison > 0) {
|
|
std::swap(perm_[dim0], perm_[dim1]);
|
|
dim1 = dim0;
|
|
} else if (comparison < 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// perform re-ordering of shape and strides
|
|
permute_dimensions<N>(stride_size, perm_, strides_array, shape_);
|
|
}
|
|
|
|
static inline std::vector<int64_t> compatible_stride(
|
|
const std::vector<int64_t>* shape_,
|
|
const int64_t& ndim,
|
|
const int64_t& element_size) {
|
|
std::vector<int64_t> stride;
|
|
int64_t next_stride = element_size;
|
|
|
|
for (int64_t dim = 0; dim < ndim; ++dim) {
|
|
stride.push_back(next_stride);
|
|
next_stride *= (*shape_)[dim];
|
|
}
|
|
return stride;
|
|
}
|
|
|
|
template <int N>
|
|
static inline void allocate_or_resize_outputs(
|
|
const std::vector<int64_t>* shape_,
|
|
const int64_t element_size,
|
|
const int64_t ndim,
|
|
std::array<int64_t*, N>* strides_array) {
|
|
std::vector<int64_t> stride_bytes =
|
|
compatible_stride(shape_, ndim, static_cast<int64_t>(element_size));
|
|
|
|
if (strides_array && (*strides_array)[0]) {
|
|
std::copy(stride_bytes.begin(), stride_bytes.end(), (*strides_array)[0]);
|
|
}
|
|
}
|
|
|
|
template <int N>
|
|
static inline void coalesce_dimensions(const int64_t& ndim,
|
|
std::array<int64_t*, N>* strides_array,
|
|
std::vector<int64_t>* stride_size,
|
|
std::vector<int64_t>* shape_) {
|
|
if (ndim <= 1) {
|
|
return;
|
|
}
|
|
|
|
// We can coalesce two adjacent dimensions if either dim has size 1 or if:
|
|
// shape[n] * stride[n] == stride[n + 1].
|
|
auto can_coalesce = [&](int dim0, int dim1) {
|
|
auto shape0 = (*shape_)[dim0];
|
|
auto shape1 = (*shape_)[dim1];
|
|
if (shape0 == 1 || shape1 == 1) {
|
|
return true;
|
|
}
|
|
for (int64_t i = 0; i < N; i++) {
|
|
auto& stride = (*strides_array)[i];
|
|
if (shape0 * stride[dim0] != stride[dim1]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// replace each operands stride at dim0 with its stride at dim1
|
|
auto replace_stride = [&](int dim0, int dim1) {
|
|
for (int64_t i = 0; i < N; i++) {
|
|
auto& stride = (*strides_array)[i];
|
|
stride[dim0] = stride[dim1];
|
|
}
|
|
};
|
|
|
|
int prev_dim = 0;
|
|
for (int64_t dim = 1; dim < ndim; dim++) {
|
|
if (can_coalesce(prev_dim, dim)) {
|
|
if ((*shape_)[prev_dim] == 1) {
|
|
replace_stride(prev_dim, dim);
|
|
}
|
|
(*shape_)[prev_dim] *= (*shape_)[dim];
|
|
} else {
|
|
prev_dim++;
|
|
if (prev_dim != dim) {
|
|
replace_stride(prev_dim, dim);
|
|
(*shape_)[prev_dim] = (*shape_)[dim];
|
|
}
|
|
}
|
|
}
|
|
(*shape_).resize(prev_dim + 1);
|
|
for (int64_t i = 0; i < N; i++) {
|
|
(*stride_size)[i] = shape_->size();
|
|
}
|
|
}
|
|
|
|
template <int N>
|
|
static inline void CopyStride(
|
|
const std::vector<int64_t>& output_dims, // value_tensor
|
|
const std::vector<int64_t>& output_strides,
|
|
const int64_t& output_elesize,
|
|
const std::vector<int64_t>& input_dims, // input_tensor
|
|
const std::vector<int64_t>& input_strides,
|
|
const int64_t& input_elesize,
|
|
std::vector<int64_t>* desired_shape,
|
|
std::array<int64_t*, N>* strides_array,
|
|
int64_t* numel,
|
|
std::array<std::vector<int64_t>, N>& strides_vec) { // NOLINT
|
|
int ndim = output_dims.size();
|
|
|
|
std::vector<int64_t> stride_size;
|
|
|
|
*desired_shape = compute_shapes({input_dims, output_dims});
|
|
strides_vec[0] = compute_strides(output_dims, // input_tensor
|
|
output_strides,
|
|
output_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[1] = compute_strides(input_dims, // value_tensor
|
|
input_strides,
|
|
input_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
for (size_t i = 0; i < N; i++) {
|
|
(*strides_array)[i] = strides_vec[i].data();
|
|
}
|
|
reorder_dimensions<N>(stride_size, desired_shape, strides_array);
|
|
|
|
coalesce_dimensions<N>(ndim, strides_array, &stride_size, desired_shape);
|
|
|
|
int64_t num = 1;
|
|
for (size_t i = 0; i < desired_shape->size(); i++) {
|
|
num *= (*desired_shape)[i];
|
|
}
|
|
*numel = num;
|
|
}
|
|
|
|
template <int N>
|
|
static inline void IndexPutStride(
|
|
const std::vector<int64_t>& output_dims, // input_tensor
|
|
const std::vector<int64_t>& output_strides,
|
|
const int64_t& output_elesize,
|
|
const std::vector<int64_t>& input_dims, // value_tensor
|
|
const std::vector<int64_t>& input_strides,
|
|
const int64_t& input_elesize,
|
|
const std::vector<int64_t>& index_dims, // index_tensor
|
|
const std::vector<int64_t>& index_strides,
|
|
const int64_t& index_elesize,
|
|
std::vector<int64_t>* desired_shape,
|
|
std::array<int64_t*, N>* strides_array,
|
|
int64_t* numel,
|
|
std::array<std::vector<int64_t>, N>& strides_vec) { // NOLINT
|
|
int ndim = output_dims.size();
|
|
|
|
std::vector<int64_t> stride_size;
|
|
|
|
*desired_shape = compute_shapes({input_dims, output_dims, index_dims});
|
|
strides_vec[0] = compute_strides(output_dims, // input_tensor
|
|
output_strides,
|
|
output_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[1] = compute_strides(input_dims, // value_tensor
|
|
input_strides,
|
|
input_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[2] = compute_strides(index_dims, // index_tensor
|
|
index_strides,
|
|
index_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
for (size_t i = 0; i < N; i++) {
|
|
(*strides_array)[i] = strides_vec[i].data();
|
|
}
|
|
reorder_dimensions<N>(stride_size, desired_shape, strides_array);
|
|
|
|
coalesce_dimensions<N>(ndim, strides_array, &stride_size, desired_shape);
|
|
|
|
int64_t num = 1;
|
|
for (size_t i = 0; i < desired_shape->size(); i++) {
|
|
num *= (*desired_shape)[i];
|
|
}
|
|
*numel = num;
|
|
}
|
|
|
|
template <int N>
|
|
static inline void IndexGetStride(
|
|
const std::vector<int64_t>& output_dims,
|
|
const std::vector<int64_t>& output_strides,
|
|
const int64_t& output_elesize,
|
|
const std::vector<int64_t>& input_dims,
|
|
const std::vector<int64_t>& input_strides,
|
|
const int64_t& input_elesize,
|
|
const std::vector<int64_t>& index_dims,
|
|
const std::vector<int64_t>& index_strides,
|
|
const int64_t& index_elesize,
|
|
std::vector<int64_t>* desired_shape,
|
|
std::array<int64_t*, N>* strides_array,
|
|
int64_t* numel,
|
|
std::array<std::vector<int64_t>, N>& strides_vec) { // NOLINT
|
|
int ndim = output_dims.size();
|
|
|
|
std::vector<int64_t> stride_size;
|
|
|
|
*desired_shape = compute_shapes({input_dims, output_dims, index_dims});
|
|
|
|
strides_vec[0] = compute_strides(input_dims,
|
|
input_strides,
|
|
input_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[1] = compute_strides(output_dims,
|
|
output_strides,
|
|
output_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[2] = compute_strides(index_dims,
|
|
index_strides,
|
|
index_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
for (size_t i = 0; i < N; i++) {
|
|
(*strides_array)[i] = strides_vec[i].data();
|
|
}
|
|
reorder_dimensions<N>(stride_size, desired_shape, strides_array);
|
|
|
|
allocate_or_resize_outputs<N>(
|
|
desired_shape, output_elesize, ndim, strides_array);
|
|
|
|
coalesce_dimensions<N>(ndim, strides_array, &stride_size, desired_shape);
|
|
|
|
int64_t num = 1;
|
|
for (size_t i = 0; i < desired_shape->size(); i++) {
|
|
num *= (*desired_shape)[i];
|
|
}
|
|
*numel = num;
|
|
}
|
|
|
|
static inline void cal_shape_stride(const std::vector<int64_t>& index_dims,
|
|
int64_t* num_indices,
|
|
std::vector<int64_t>* shape_tmp,
|
|
std::vector<int64_t>* stride_tmp) {
|
|
std::vector<int64_t> index_dims_;
|
|
std::vector<int64_t> index_stride_;
|
|
|
|
bool tmp_flag = false;
|
|
for (unsigned i = 0; i < index_dims.size(); i++) {
|
|
if (index_dims[i] == -1) {
|
|
if (!tmp_flag) {
|
|
*num_indices = i;
|
|
tmp_flag = true;
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!tmp_flag) {
|
|
index_dims_.push_back(index_dims[i]);
|
|
} else {
|
|
shape_tmp->push_back(index_dims[i]);
|
|
}
|
|
}
|
|
|
|
int shape_size = shape_tmp->size();
|
|
stride_tmp->resize(shape_size);
|
|
if (shape_size > 0) {
|
|
(*stride_tmp)[shape_size - 1] = 1;
|
|
}
|
|
if (shape_size > 1) {
|
|
for (int i = shape_size - 2; i >= 0; i--) {
|
|
(*stride_tmp)[i] = (*stride_tmp)[i + 1] * (*shape_tmp)[i + 1];
|
|
}
|
|
}
|
|
}
|
|
|
|
template <int N>
|
|
static inline void ScatterAddStride(
|
|
const std::vector<int64_t>& output_dims,
|
|
const std::vector<int64_t>& output_strides,
|
|
const int64_t& output_elesize,
|
|
const std::vector<int64_t>& input_dims,
|
|
const std::vector<int64_t>& input_strides,
|
|
const int64_t& input_elesize,
|
|
const std::vector<int64_t>& index_dims,
|
|
const std::vector<int64_t>& index_strides,
|
|
const int64_t& index_elesize,
|
|
std::vector<int64_t>* desired_shape,
|
|
std::array<int64_t*, N>* strides_array,
|
|
int64_t* numel,
|
|
std::array<std::vector<int64_t>, N>& strides_vec) { // NOLINT
|
|
int ndim = output_dims.size();
|
|
|
|
std::vector<int64_t> stride_size;
|
|
|
|
*desired_shape = compute_shapes({input_dims, output_dims, index_dims});
|
|
|
|
strides_vec[0] = compute_strides(input_dims,
|
|
input_strides,
|
|
input_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[1] = compute_strides(output_dims,
|
|
output_strides,
|
|
output_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
strides_vec[2] = compute_strides(index_dims,
|
|
index_strides,
|
|
index_elesize,
|
|
ndim,
|
|
desired_shape,
|
|
&stride_size);
|
|
|
|
for (size_t i = 0; i < N; i++) {
|
|
(*strides_array)[i] = strides_vec[i].data();
|
|
}
|
|
|
|
reorder_dimensions<N>(stride_size, desired_shape, strides_array);
|
|
|
|
coalesce_dimensions<N>(ndim, strides_array, &stride_size, desired_shape);
|
|
|
|
int64_t num = 1;
|
|
for (size_t i = 0; i < desired_shape->size(); i++) {
|
|
num *= (*desired_shape)[i];
|
|
}
|
|
*numel = num;
|
|
}
|
|
|
|
static inline bool hasContiguousSubspace(const std::vector<DenseTensor>& tl) {
|
|
auto isDefined = [](const DenseTensor& tensor) {
|
|
return tensor.initialized();
|
|
};
|
|
auto isNull = [](const DenseTensor& tensor) { return !tensor.initialized(); };
|
|
|
|
auto start = std::find_if(tl.begin(), tl.end(), isDefined);
|
|
auto stop = std::find_if(tl.rbegin(), tl.rend(), isDefined);
|
|
auto it = std::find_if(start, stop.base(), isNull);
|
|
return it == stop.base();
|
|
}
|
|
|
|
#if defined(PADDLE_WITH_CUDA)
|
|
|
|
static inline std::vector<DenseTensor> expandTensors(
|
|
const GPUContext& dev_ctx, const std::vector<const DenseTensor*>& indices) {
|
|
std::vector<DenseTensor> result;
|
|
for (const auto& index : indices) {
|
|
if (index == nullptr) {
|
|
result.emplace_back();
|
|
continue;
|
|
}
|
|
|
|
if (index->dtype() == DataType::BOOL) {
|
|
DenseTensor bool_2_idx;
|
|
phi::NonZeroKernel<bool, GPUContext>(dev_ctx, *index, &bool_2_idx);
|
|
|
|
for (int j = 0; j < index->dims().size(); ++j) {
|
|
DenseTensor sliced_tensor;
|
|
phi::SliceKernel<int64_t, GPUContext>(
|
|
dev_ctx, bool_2_idx, {1}, {j}, {j + 1}, {1}, {}, &sliced_tensor);
|
|
result.emplace_back(sliced_tensor);
|
|
}
|
|
} else {
|
|
result.emplace_back(*index);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static inline std::vector<DenseTensor> expand_outplace(
|
|
const GPUContext& dev_ctx, const std::vector<DenseTensor>& to_expand) {
|
|
bool first = true;
|
|
DDim target_shape;
|
|
for (size_t i = 0; i < to_expand.size(); ++i) {
|
|
if (!to_expand[i].initialized()) continue;
|
|
if (first) {
|
|
target_shape = to_expand[i].dims();
|
|
first = false;
|
|
} else {
|
|
target_shape = InferSizeSymdimvector(target_shape, to_expand[i].dims());
|
|
}
|
|
}
|
|
|
|
std::vector<DenseTensor> result(to_expand.size());
|
|
for (size_t i = 0; i < to_expand.size(); ++i) {
|
|
if (!to_expand[i].initialized()) continue;
|
|
if (to_expand[i].dims() == target_shape) {
|
|
result[i] = to_expand[i];
|
|
} else {
|
|
phi::ExpandKernel<float, GPUContext>(
|
|
dev_ctx,
|
|
to_expand[i],
|
|
phi::IntArray(vectorize<int64_t>(target_shape)),
|
|
&result[i]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
inline std::tuple<DenseTensor, std::vector<DenseTensor>, std::vector<int64_t>>
|
|
transposeToFrontAndInvPerm(const GPUContext& dev_ctx,
|
|
const DenseTensor& self,
|
|
const std::vector<DenseTensor>& indices) {
|
|
std::vector<int> dims;
|
|
std::vector<int64_t> inv_perm;
|
|
std::vector<DenseTensor> transposed_indices;
|
|
dims.reserve(self.dims().size());
|
|
inv_perm.resize(self.dims().size());
|
|
|
|
for (int i = 0; i < static_cast<int>(self.dims().size()); ++i) {
|
|
if (indices[i].initialized()) {
|
|
dims.push_back(i);
|
|
transposed_indices.emplace_back(indices[i]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < static_cast<int>(self.dims().size()); ++i) {
|
|
if (!indices[i].initialized()) {
|
|
dims.push_back(i);
|
|
transposed_indices.emplace_back();
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < static_cast<int>(self.dims().size()); ++i) {
|
|
inv_perm[dims[i]] = i;
|
|
}
|
|
|
|
DenseTensor transposed_self;
|
|
TransposeKernel<T, GPUContext>(dev_ctx, self, dims, &transposed_self);
|
|
|
|
return std::make_tuple(transposed_self, transposed_indices, inv_perm);
|
|
}
|
|
|
|
static inline std::vector<int64_t> computeLinearStride(
|
|
const DenseTensor& tensor) {
|
|
auto sizes = vectorize<int64_t>(tensor.dims());
|
|
std::vector<int64_t> stride(sizes.size());
|
|
if (stride.empty()) {
|
|
return stride;
|
|
}
|
|
stride.back() = 1;
|
|
std::partial_sum(sizes.rbegin(),
|
|
sizes.rend() - 1,
|
|
stride.rbegin() + 1,
|
|
std::multiplies<int64_t>());
|
|
return stride;
|
|
}
|
|
|
|
static inline DenseTensor wrapIndexOnce(const GPUContext& dev_ctx,
|
|
const DenseTensor& index,
|
|
const int64_t& dim,
|
|
const int64_t& dim_size,
|
|
bool check_range) {
|
|
DenseTensor dim_size_tensor;
|
|
dim_size_tensor.Resize(index.dims());
|
|
dev_ctx.Alloc<int64_t>(&dim_size_tensor);
|
|
|
|
auto* dim_size_data = dim_size_tensor.data<int64_t>();
|
|
auto numel = index.numel();
|
|
std::vector<int64_t> host_data(numel, dim_size);
|
|
const int64_t* stable_hd =
|
|
phi::backends::gpu::RestoreHostMemIfCapturingCUDAGraph(host_data.data(),
|
|
host_data.size());
|
|
phi::memory_utils::Copy(dev_ctx.GetPlace(),
|
|
dim_size_data,
|
|
CPUPlace(),
|
|
stable_hd,
|
|
numel * sizeof(int64_t),
|
|
dev_ctx.stream());
|
|
|
|
return phi::Remainder<int64_t>(dev_ctx, index, dim_size_tensor);
|
|
}
|
|
|
|
static inline std::tuple<DenseTensor, int64_t, int64_t, int64_t>
|
|
computeLinearIndex(const GPUContext& dev_ctx,
|
|
const DenseTensor& src,
|
|
const std::vector<DenseTensor>& indices,
|
|
bool check_range) {
|
|
std::vector<int64_t> strides = computeLinearStride(src);
|
|
DenseTensor linearIndex;
|
|
int64_t nElemBefore = 1, nElemAfter = 1, strideBefore = 0;
|
|
|
|
for (int64_t i = 0; i < src.dims().size(); ++i) {
|
|
if (indices[i].initialized()) {
|
|
auto wrapped_index =
|
|
wrapIndexOnce(dev_ctx, indices[i], i, src.dims()[i], check_range);
|
|
|
|
auto strides_tensor = phi::Full<int64_t, GPUContext>(
|
|
dev_ctx,
|
|
vectorize<int64_t>(wrapped_index.dims()),
|
|
phi::Scalar(strides[i]));
|
|
|
|
auto scaled_index = phi::Multiply<int64_t, GPUContext>(
|
|
dev_ctx, wrapped_index, strides_tensor);
|
|
|
|
if (linearIndex.initialized()) {
|
|
phi::AddKernel<int64_t, GPUContext>(
|
|
dev_ctx, linearIndex, scaled_index, &linearIndex);
|
|
} else {
|
|
linearIndex = scaled_index;
|
|
if (i > 0) {
|
|
strideBefore = src.strides()[i - 1];
|
|
}
|
|
}
|
|
} else if (linearIndex.initialized()) {
|
|
nElemAfter *= src.dims()[i];
|
|
} else {
|
|
nElemBefore *= src.dims()[i];
|
|
}
|
|
}
|
|
|
|
return std::make_tuple(
|
|
std::move(linearIndex), nElemBefore, strideBefore, nElemAfter);
|
|
}
|
|
|
|
template <typename T>
|
|
static inline std::tuple<DenseTensor,
|
|
DenseTensor,
|
|
int64_t,
|
|
int64_t,
|
|
int64_t,
|
|
std::vector<int64_t>>
|
|
makeLinearIndex(const GPUContext& dev_ctx,
|
|
const DenseTensor& self,
|
|
const std::vector<const DenseTensor*>& orig,
|
|
bool check_range) {
|
|
auto indices = expandTensors(dev_ctx, orig);
|
|
for (auto& idx : indices) {
|
|
if (idx.initialized() && idx.dtype() == DataType::INT32) {
|
|
idx = Cast<int32_t, GPUContext>(dev_ctx, idx, DataType::INT64);
|
|
}
|
|
}
|
|
indices = expand_outplace(dev_ctx, std::move(indices));
|
|
|
|
while (indices.size() < static_cast<size_t>(self.dims().size())) {
|
|
indices.emplace_back();
|
|
}
|
|
|
|
std::vector<int64_t> inverse_perm;
|
|
DenseTensor transposed_self = self;
|
|
std::vector<DenseTensor> transposed_indices;
|
|
std::vector<int64_t> inv_perm;
|
|
if (!hasContiguousSubspace(indices)) {
|
|
auto [tmp_self, tmp_indices, tmp_perm] =
|
|
transposeToFrontAndInvPerm<T>(dev_ctx, self, indices);
|
|
transposed_self = std::move(tmp_self);
|
|
transposed_indices = std::move(tmp_indices);
|
|
inv_perm = std::move(tmp_perm);
|
|
} else {
|
|
transposed_indices = indices;
|
|
}
|
|
|
|
auto [linear_index, n_elem_before, stride_before, n_elem_after] =
|
|
computeLinearIndex(
|
|
dev_ctx, transposed_self, transposed_indices, check_range);
|
|
|
|
return std::make_tuple(linear_index,
|
|
transposed_self,
|
|
n_elem_before,
|
|
stride_before,
|
|
n_elem_after,
|
|
inv_perm);
|
|
}
|
|
|
|
#endif
|
|
inline bool are_expandable(const std::vector<int64_t>& shape1,
|
|
const std::vector<int64_t>& shape2) {
|
|
size_t ndim1 = shape1.size();
|
|
size_t ndim2 = shape2.size();
|
|
size_t ndim = std::min(ndim1, ndim2);
|
|
|
|
for (int64_t i = static_cast<int64_t>(ndim) - 1; i >= 0; --i) {
|
|
auto dim1 = shape1[--ndim1];
|
|
auto dim2 = shape2[--ndim2];
|
|
if (dim1 == dim2 || dim1 == 1 || dim2 == 1) {
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline int64_t LargestIndex(const DenseTensor& tensor) {
|
|
int64_t result = 0;
|
|
const auto& dims = tensor.dims();
|
|
const auto& strides = tensor.strides();
|
|
|
|
for (int i = 0; i < dims.size(); ++i) {
|
|
result += (dims[i] - 1) * strides[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
inline int GetNumBits(uint64_t max_val) {
|
|
if (max_val == 0) return 1;
|
|
|
|
int num_bits = 1;
|
|
while (max_val > 1) {
|
|
max_val >>= 1;
|
|
num_bits++;
|
|
}
|
|
return num_bits;
|
|
}
|
|
} // namespace funcs
|
|
} // namespace phi
|