Files
paddlepaddle--paddle/paddle/phi/kernels/xpu/unique_kernel.cc
T
2026-07-13 12:40:42 +08:00

429 lines
16 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.
#include <climits>
#include <numeric>
#include <utility>
#include <vector>
#include "paddle/phi/kernels/unique_kernel.h"
#include "paddle/phi/backends/xpu/enforce_xpu.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/utils/data_type.h"
#include "paddle/phi/core/visit_type.h"
namespace phi {
template <typename Context, typename T, typename IndexT>
void XPUFlattenUniqueKernelImpl(const Context& dev_ctx,
const DenseTensor& x,
bool return_index,
bool return_inverse,
bool return_counts,
DenseTensor* out,
DenseTensor* indices,
DenseTensor* index,
DenseTensor* counts) {
using XPUType = typename XPUTypeTrait<T>::Type;
const auto* x_data = x.data<T>();
int64_t x_len = x.numel();
int r = 0;
xpu::ctx_guard RAII_GUARD(dev_ctx.x_context());
int64_t unique_len_cpu = 0;
int64_t* unique_len_xpu = RAII_GUARD.alloc_l3_or_gm<int64_t>(1);
if (x_len != 0) {
r = xpu::unique_count<XPUType, IndexT>(
dev_ctx.x_context(),
reinterpret_cast<const XPUType*>(x_data),
unique_len_xpu,
x_len,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
false);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "unique_count");
memory_utils::Copy(CPUPlace(),
&unique_len_cpu,
dev_ctx.GetPlace(),
unique_len_xpu,
sizeof(int64_t));
}
out->Resize({unique_len_cpu});
auto* out_data = dev_ctx.template Alloc<T>(out);
IndexT* indices_data = nullptr;
if (return_index) {
indices->Resize({unique_len_cpu});
indices_data = dev_ctx.template Alloc<IndexT>(indices);
}
IndexT* inverse_data = nullptr;
if (return_inverse) {
index->Resize({x_len});
inverse_data = dev_ctx.template Alloc<IndexT>(index);
}
IndexT* counts_data = nullptr;
if (return_counts) {
counts->Resize({unique_len_cpu});
counts_data = dev_ctx.template Alloc<IndexT>(counts);
}
if (x_len == 0) {
return;
}
r = xpu::unique_compute<XPUType, IndexT>(
dev_ctx.x_context(),
reinterpret_cast<const XPUType*>(x_data),
reinterpret_cast<XPUType*>(out_data),
x_len,
unique_len_cpu,
indices_data,
counts_data,
inverse_data,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
false);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "unique_compute");
}
template <typename Context, typename T, typename IndexT>
void XPUDimUniqueKernelImpl(const Context& dev_ctx,
const DenseTensor& x,
bool return_index,
bool return_inverse,
bool return_counts,
int axis,
DenseTensor* out,
DenseTensor* indices,
DenseTensor* index,
DenseTensor* counts) {
using XPUType = typename XPUTypeTrait<T>::Type;
xpu::ctx_guard RAII_GUARD(dev_ctx.x_context());
int r = 0;
const auto* x_data = x.data<T>();
auto* x_trans_data = RAII_GUARD.alloc_l3_or_gm<XPUType>(x.numel());
std::vector<int64_t> permute(x.dims().size());
std::iota(permute.begin(), permute.end(), 0);
permute[axis] = 0;
permute[0] = axis;
if (axis != 0) {
auto x_shape = vectorize<int64_t>(x.dims());
r = xpu::transpose<XPUType>(dev_ctx.x_context(),
reinterpret_cast<const XPUType*>(x_data),
x_trans_data,
x_shape,
permute);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "transpose");
} else {
r = xpu::copy<XPUType>(dev_ctx.x_context(),
reinterpret_cast<const XPUType*>(x_data),
x_trans_data,
x.numel());
PADDLE_ENFORCE_XDNN_SUCCESS(r, "copy");
}
DDim x_trans_dims = x.dims();
x_trans_dims[0] = x.dims()[axis];
x_trans_dims[axis] = x.dims()[0];
DDim x_trans_flat_dims = common::flatten_to_2d(x_trans_dims, 1);
int64_t axis_len = x_trans_flat_dims[0];
int64_t slice_size = x_trans_flat_dims[1];
auto x_trans_flat_dims_vec = vectorize<int64_t>(x_trans_flat_dims);
auto* sorted_axis_idx = RAII_GUARD.alloc_l3_or_gm<IndexT>(axis_len);
auto* sort_in_tmp = RAII_GUARD.alloc_l3_or_gm<XPUType>(axis_len);
auto* sort_out_tmp = RAII_GUARD.alloc_l3_or_gm<XPUType>(axis_len);
auto* x_trans_tmp = RAII_GUARD.alloc_l3_or_gm<XPUType>(x.numel());
auto* ori_idx_xpu = RAII_GUARD.alloc_l3_or_gm<IndexT>(axis_len);
auto* ori_idx_xpu_tmp = RAII_GUARD.alloc_l3_or_gm<IndexT>(axis_len);
auto* sort_offset = RAII_GUARD.alloc_l3_or_gm<IndexT>(axis_len);
r = xpu::range<IndexT>(
dev_ctx.x_context(), sort_offset, 0, slice_size, axis_len);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "range");
r = xpu::range<IndexT>(dev_ctx.x_context(), ori_idx_xpu, 0, 1, axis_len);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "range");
// radix sort
for (int64_t i = slice_size - 1; i >= 0; --i) {
r = xpu::paddle_gather<XPUType, IndexT>(dev_ctx.x_context(),
x_trans_data + i,
sort_offset,
sort_in_tmp,
{x.numel() - i},
axis_len,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
r = xpu::stable_sort<XPUType, IndexT>(dev_ctx.x_context(),
sort_in_tmp,
sort_out_tmp,
sorted_axis_idx,
1,
axis_len,
false);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "stable_sort");
r = xpu::paddle_gather<XPUType, IndexT>(dev_ctx.x_context(),
x_trans_data,
sorted_axis_idx,
x_trans_tmp,
x_trans_flat_dims_vec,
axis_len,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
std::swap(x_trans_data, x_trans_tmp);
r = xpu::paddle_gather<IndexT, IndexT>(dev_ctx.x_context(),
ori_idx_xpu,
sorted_axis_idx,
ori_idx_xpu_tmp,
{axis_len},
axis_len,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
std::swap(ori_idx_xpu, ori_idx_xpu_tmp);
}
// adjacent difference
int64_t compare_num = (axis_len - 1) * slice_size;
auto* compare_results = RAII_GUARD.alloc_l3_or_gm<bool>(compare_num);
if (compare_num > 0) {
r = xpu::broadcast_equal<XPUType>(dev_ctx.x_context(),
x_trans_data + slice_size,
x_trans_data,
compare_results,
{compare_num},
{compare_num});
PADDLE_ENFORCE_XDNN_SUCCESS(r, "broadcast_equal");
}
std::vector<IndexT> unique_axis;
std::vector<IndexT> indices_cpu;
std::vector<IndexT> inverse_cpu(axis_len);
std::vector<IndexT> counts_cpu;
std::vector<IndexT> ori_idx_cpu(axis_len);
memory_utils::Copy(CPUPlace(),
ori_idx_cpu.data(),
dev_ctx.GetPlace(),
ori_idx_xpu,
sizeof(IndexT) * axis_len);
unique_axis.push_back(0);
indices_cpu.push_back(ori_idx_cpu[0]);
inverse_cpu[ori_idx_cpu[0]] = 0;
IndexT unique_len = 1;
IndexT repeat_cnt = 1;
if (axis_len > 1) {
DenseTensor adj_identical_cpu;
adj_identical_cpu.Resize({axis_len - 1});
bool* adj_identical_cpu_data =
dev_ctx.template HostAlloc<bool>(&adj_identical_cpu);
auto* adj_identical_xpu = RAII_GUARD.alloc_l3_or_gm<bool>(axis_len - 1);
r = xpu::reduce_all<bool>(dev_ctx.x_context(),
compare_results,
adj_identical_xpu,
{axis_len - 1, slice_size},
{1});
PADDLE_ENFORCE_XDNN_SUCCESS(r, "reduce_all");
memory_utils::Copy(CPUPlace(),
adj_identical_cpu_data,
dev_ctx.GetPlace(),
adj_identical_xpu,
(axis_len - 1) * sizeof(bool));
for (IndexT i = 1; i < axis_len; ++i) {
if (!adj_identical_cpu_data[i - 1]) {
unique_axis.push_back(i);
indices_cpu.push_back(ori_idx_cpu[i]);
counts_cpu.push_back(repeat_cnt);
++unique_len;
repeat_cnt = 1;
} else {
++repeat_cnt;
}
inverse_cpu[ori_idx_cpu[i]] = unique_len - 1;
}
}
counts_cpu.push_back(repeat_cnt);
DDim out_dims = x.dims();
out_dims[axis] = unique_len;
out->Resize(out_dims);
auto* out_data = dev_ctx.template Alloc<T>(out);
auto* unique_axis_idx_xpu = RAII_GUARD.alloc_l3_or_gm<IndexT>(unique_len);
auto* out_trans_data =
RAII_GUARD.alloc_l3_or_gm<XPUType>(unique_len * slice_size);
memory_utils::Copy(dev_ctx.GetPlace(),
unique_axis_idx_xpu,
CPUPlace(),
unique_axis.data(),
unique_len * sizeof(IndexT));
r = xpu::paddle_gather<XPUType, IndexT>(dev_ctx.x_context(),
x_trans_data,
unique_axis_idx_xpu,
out_trans_data,
x_trans_flat_dims_vec,
unique_len,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
DDim out_trans_dims = x_trans_dims;
out_trans_dims[0] = unique_len;
auto out_trans_dims_vec = vectorize<int64_t>(out_trans_dims);
if (axis != 0) {
r = xpu::transpose<XPUType>(dev_ctx.x_context(),
out_trans_data,
reinterpret_cast<XPUType*>(out_data),
out_trans_dims_vec,
permute);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "transpose");
} else {
r = xpu::copy<XPUType>(dev_ctx.x_context(),
out_trans_data,
reinterpret_cast<XPUType*>(out_data),
unique_len * slice_size);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "copy");
}
if (return_index) {
indices->Resize({unique_len});
auto* indices_data = dev_ctx.template Alloc<IndexT>(indices);
memory_utils::Copy(dev_ctx.GetPlace(),
indices_data,
CPUPlace(),
indices_cpu.data(),
sizeof(IndexT) * unique_len);
}
if (return_inverse) {
index->Resize({axis_len});
auto* reverse_data = dev_ctx.template Alloc<IndexT>(index);
memory_utils::Copy(dev_ctx.GetPlace(),
reverse_data,
CPUPlace(),
inverse_cpu.data(),
sizeof(IndexT) * axis_len);
}
if (return_counts) {
counts->Resize({unique_len});
auto* counts_data = dev_ctx.template Alloc<IndexT>(counts);
memory_utils::Copy(dev_ctx.GetPlace(),
counts_data,
CPUPlace(),
counts_cpu.data(),
sizeof(IndexT) * unique_len);
}
}
template <typename T, typename Context>
void UniqueKernel(const Context& dev_ctx,
const DenseTensor& x,
bool return_index,
bool return_inverse,
bool return_counts,
const std::vector<int>& axis,
DataType dtype,
DenseTensor* out,
DenseTensor* indices,
DenseTensor* index,
DenseTensor* counts) {
bool is_sorted = true;
UniqueRawKernel<T, Context>(dev_ctx,
x,
return_index,
return_inverse,
return_counts,
axis,
dtype,
is_sorted,
out,
indices,
index,
counts);
}
template <typename T, typename Context>
void UniqueRawKernel(const Context& dev_ctx,
const DenseTensor& x,
bool return_index,
bool return_inverse,
bool return_counts,
const std::vector<int>& axis,
DataType dtype,
bool is_sorted,
DenseTensor* out,
DenseTensor* indices,
DenseTensor* index,
DenseTensor* counts) {
if (dtype == DataType::INT32) {
PADDLE_ENFORCE_LE(
x.numel(),
INT_MAX,
common::errors::InvalidArgument(
"The number of elements in Input(X) should be less than or "
"equal to INT_MAX, but received num is %d. Please set `dtype` to "
"int64.",
x.numel()));
}
if (axis.empty()) {
PD_VISIT_BASE_INTEGRAL_TYPES(dtype, "XPUFlattenUniqueKernelImpl", [&] {
XPUFlattenUniqueKernelImpl<Context, T, data_t>(dev_ctx,
x,
return_index,
return_inverse,
return_counts,
out,
indices,
index,
counts);
});
} else {
int axis_value = axis[0];
axis_value = (axis_value == -1) ? (x.dims().size() - 1) : axis_value;
PD_VISIT_BASE_INTEGRAL_TYPES(dtype, "XPUDimUniqueKernelImpl", [&] {
XPUDimUniqueKernelImpl<Context, T, data_t>(dev_ctx,
x,
return_index,
return_inverse,
return_counts,
axis_value,
out,
indices,
index,
counts);
});
}
}
} // namespace phi
PD_REGISTER_KERNEL(
unique, XPU, ALL_LAYOUT, phi::UniqueKernel, float, int, int64_t) {
kernel->OutputAt(1).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(2).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(3).SetDataType(phi::DataType::UNDEFINED);
}
PD_REGISTER_KERNEL(
unique_raw, XPU, ALL_LAYOUT, phi::UniqueRawKernel, float, int, int64_t) {
kernel->OutputAt(1).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(2).SetDataType(phi::DataType::UNDEFINED);
kernel->OutputAt(3).SetDataType(phi::DataType::UNDEFINED);
}