Files
paddlepaddle--paddle/paddle/phi/api/lib/data_transform.cc
T
2026-07-13 12:40:42 +08:00

1121 lines
44 KiB
C++

/* Copyright (c) 2022 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/api/lib/data_transform.h"
#include <sstream>
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/api/lib/utils/allocator.h"
#include "paddle/phi/backends/context_pool.h"
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function_registry.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/core/visit_type.h"
#include "paddle/phi/kernels/cast_kernel.h"
#include "paddle/phi/kernels/contiguous_kernel.h"
#include "paddle/phi/kernels/transfer_layout_kernel.h"
PHI_DECLARE_bool(use_stride_kernel);
namespace paddle::experimental {
inline bool NeedTransformDataType(const DataType& input,
const DataType& target,
const TransformFlag& transform_flag) {
return input != target &&
(transform_flag.need_trans_data_type() ||
((target == DataType::COMPLEX64 || target == DataType::COMPLEX128) &&
(input != DataType::INT32 && input != DataType::INT64 &&
input != DataType::BOOL)));
}
inline bool NeedTransformLayout(const DataLayout& input,
const DataLayout& target,
const phi::Place& place,
const TransformFlag& transform_flag) {
if (FLAGS_use_stride_kernel && target == DataLayout::STRIDED) {
return false;
}
bool ret = transform_flag.need_trans_layout() &&
(input != DataLayout::ALL_LAYOUT &&
target != DataLayout::ALL_LAYOUT && input != target);
if (place.GetType() == phi::AllocationType::GPU) {
return false;
}
return ret;
}
inline bool NeedTransform2Contiguous(bool is_stride_kernel,
bool is_contiguous) {
return FLAGS_use_stride_kernel && !is_stride_kernel && !is_contiguous;
}
inline phi::DenseTensor TransDataLayout(const phi::DenseTensor& tensor,
DataLayout layout) {
auto& pool = phi::DeviceContextPool::Instance();
VLOG(3) << "DataLayoutTransform src_layout: " << tensor.layout()
<< " dst_layout: " << layout;
if (tensor.place().GetType() == phi::AllocationType::CPU) {
auto* dev_ctx = static_cast<phi::CPUContext*>(pool.Get(tensor.place()));
return phi::TransferLayout(*dev_ctx, tensor, layout);
} else {
PADDLE_THROW(common::errors::PreconditionNotMet(
"Unsupported data layout cast from CPU to GPU."));
}
return tensor;
}
template <typename Context>
phi::DenseTensor CastDataType(const Context& dev_ctx,
const phi::DenseTensor& tensor,
DataType dtype) {
switch (tensor.dtype()) {
case DataType::FLOAT32:
return phi::Cast<float>(dev_ctx, tensor, dtype);
case DataType::FLOAT64:
return phi::Cast<double>(dev_ctx, tensor, dtype);
case DataType::INT32:
return phi::Cast<int32_t>(dev_ctx, tensor, dtype);
case DataType::INT64:
return phi::Cast<int64_t>(dev_ctx, tensor, dtype);
case DataType::FLOAT16:
return phi::Cast<phi::dtype::float16>(dev_ctx, tensor, dtype);
case DataType::BFLOAT16:
return phi::Cast<phi::dtype::bfloat16>(dev_ctx, tensor, dtype);
case DataType::BOOL:
return phi::Cast<bool>(dev_ctx, tensor, dtype);
case DataType::INT16:
return phi::Cast<int16_t>(dev_ctx, tensor, dtype);
case DataType::UINT8:
return phi::Cast<uint8_t>(dev_ctx, tensor, dtype);
default:
PADDLE_THROW(common::errors::Unimplemented(
"Data type (%s) is not supported when casting data type.",
tensor.dtype()));
}
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
phi::DenseTensor CastDataType(const phi::GPUContext& dev_ctx,
const phi::DenseTensor& tensor,
DataType dtype) {
switch (tensor.dtype()) {
case DataType::FLOAT32:
return phi::Cast<float>(dev_ctx, tensor, dtype);
case DataType::FLOAT64:
return phi::Cast<double>(dev_ctx, tensor, dtype);
case DataType::INT32:
return phi::Cast<int32_t>(dev_ctx, tensor, dtype);
case DataType::INT64:
return phi::Cast<int64_t>(dev_ctx, tensor, dtype);
case DataType::FLOAT16:
return phi::Cast<phi::dtype::float16>(dev_ctx, tensor, dtype);
case DataType::BOOL:
return phi::Cast<bool>(dev_ctx, tensor, dtype);
case DataType::INT16:
return phi::Cast<int16_t>(dev_ctx, tensor, dtype);
case DataType::UINT8:
return phi::Cast<uint8_t>(dev_ctx, tensor, dtype);
default:
PADDLE_THROW(common::errors::Unimplemented(
"Data type (%s) is not supported when casting data type.",
tensor.dtype()));
}
}
#endif
#ifdef PADDLE_WITH_XPU
phi::DenseTensor CastDataType(const phi::XPUContext& dev_ctx,
const phi::DenseTensor& tensor,
DataType dtype) {
switch (tensor.dtype()) {
case DataType::FLOAT32:
return phi::Cast<float>(dev_ctx, tensor, dtype);
case DataType::FLOAT64:
return phi::Cast<double>(dev_ctx, tensor, dtype);
case DataType::INT32:
return phi::Cast<int32_t>(dev_ctx, tensor, dtype);
case DataType::INT64:
return phi::Cast<int64_t>(dev_ctx, tensor, dtype);
case DataType::FLOAT16:
return phi::Cast<phi::dtype::float16>(dev_ctx, tensor, dtype);
case DataType::BOOL:
return phi::Cast<bool>(dev_ctx, tensor, dtype);
case DataType::UINT8:
return phi::Cast<uint8_t>(dev_ctx, tensor, dtype);
default:
PADDLE_THROW(common::errors::Unimplemented(
"Data type (%s) is not supported when casting data type.",
tensor.dtype()));
}
}
#endif
inline phi::DenseTensor TransDataType(const phi::DenseTensor& tensor,
DataType dtype) {
auto& pool = phi::DeviceContextPool::Instance();
VLOG(3) << "DataTypeTransform src_dtype: " << tensor.dtype()
<< " dst_dtype: " << dtype;
DefaultAllocator alloc(tensor.place());
phi::DenseTensor out(&alloc, {dtype, tensor.dims(), tensor.layout()});
if (tensor.place().GetType() == phi::AllocationType::CPU) {
auto* dev_ctx = static_cast<phi::CPUContext*>(pool.Get(tensor.place()));
return CastDataType(*dev_ctx, tensor, dtype);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
} else if (tensor.place().GetType() == phi::AllocationType::GPU) {
auto* dev_ctx = static_cast<phi::GPUContext*>(pool.Get(tensor.place()));
return CastDataType(*dev_ctx, tensor, dtype);
#endif
#ifdef PADDLE_WITH_XPU
} else if (tensor.place().GetType() == phi::AllocationType::XPU) {
auto* dev_ctx = static_cast<phi::XPUContext*>(pool.Get(tensor.place()));
return CastDataType(*dev_ctx, tensor, dtype);
#endif
#ifdef PADDLE_WITH_CUSTOM_DEVICE
} else if (tensor.place().GetType() == phi::AllocationType::CUSTOM) {
phi::DenseTensor out;
out.Resize(tensor.dims());
auto* dev_ctx = static_cast<phi::CustomContext*>(pool.Get(tensor.place()));
auto kernel_result =
phi::KernelFactory::Instance().SelectKernelOrThrowError(
"cast",
{phi::TransToPhiBackend(tensor.place()),
phi::DataLayout::ALL_LAYOUT,
tensor.dtype()});
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
phi::DataType,
phi::DenseTensor*);
const auto& kernel = kernel_result.kernel;
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx, tensor, dtype, &out);
return out;
#endif
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Place type is not supported when casting data type."));
}
return out;
}
inline phi::DenseTensor TransDataPlace(const phi::DenseTensor& tensor,
Place dst_place) {
VLOG(3) << "DeviceTransform in, src_place " << tensor.place()
<< " dst_place: " << dst_place;
auto& pool = phi::DeviceContextPool::Instance();
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
// NOTE(yy): TransDataPlace should wait for computation of input.
if (tensor.place().GetType() != phi::AllocationType::GPUPINNED) {
pool.Get(tensor.place())->Wait();
pool.Get(dst_place)->Wait();
}
#endif
// FIXME(zcd): TransDataPlace is used to transform data from GPU to CPU and
// the enforced checks have been done in GetDeviceContext, so the
// `dev_ctx->Wait()` is necessary. But `dev_ctx->Wait()` will make the program
// slow, especially when the number of elements is little, for example,
// the elements of learning rate are one and it's CPU side.
// One solution is to use a CUDA kernel to complete the copy operation when
// the transforming is from CPU to GPU and the number of elements is little.
// But the embarrassment is that this solution this solution makes training
// slower.
phi::DenseTensor out;
phi::DeviceContext* dev_ctx = nullptr;
if (dst_place.GetType() != AllocationType::CPU) {
dev_ctx = pool.Get(dst_place);
} else {
dev_ctx = pool.Get(tensor.place());
}
phi::Copy(*dev_ctx, tensor, dst_place, true, &out);
return out;
}
template <typename Context>
phi::DenseTensor TensorContiguous(const Context& dev_ctx,
const phi::DenseTensor& tensor) {
phi::DenseTensor dense_out;
phi::MetaTensor meta_input(tensor);
phi::MetaTensor meta_out(&dense_out);
UnchangedInferMeta(meta_input, &meta_out);
PD_VISIT_ALL_TYPES(tensor.dtype(), "TensorContiguous", ([&] {
phi::ContiguousKernel<data_t, Context>(
dev_ctx, tensor, &dense_out);
}));
return dense_out;
}
phi::DenseTensor Trans2Contiguous(const phi::DenseTensor& tensor) {
auto& pool = phi::DeviceContextPool::Instance();
VLOG(3) << "Trans2Contiguous...";
if (tensor.place().GetType() == phi::AllocationType::CPU) {
auto* dev_ctx = static_cast<phi::CPUContext*>(pool.Get(tensor.place()));
return TensorContiguous<phi::CPUContext>(*dev_ctx, tensor);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
} else if (tensor.place().GetType() == phi::AllocationType::GPU) {
auto* dev_ctx = static_cast<phi::GPUContext*>(pool.Get(tensor.place()));
return TensorContiguous<phi::GPUContext>(*dev_ctx, tensor);
#endif
#ifdef PADDLE_WITH_XPU
} else if (tensor.place().GetType() == phi::AllocationType::XPU) {
auto* dev_ctx = static_cast<phi::XPUContext*>(pool.Get(tensor.place()));
return TensorContiguous<phi::XPUContext>(*dev_ctx, tensor);
#endif
#ifdef PADDLE_WITH_CUSTOM_DEVICE
} else if (tensor.place().GetType() == phi::AllocationType::CUSTOM) {
auto* dev_ctx = static_cast<phi::CustomContext*>(pool.Get(tensor.place()));
phi::DenseTensor dense_out;
phi::MetaTensor meta_input(tensor);
phi::MetaTensor meta_out(&dense_out);
UnchangedInferMeta(meta_input, &meta_out);
const phi::KernelKey& kernel_key = {phi::TransToPhiBackend(tensor.place()),
phi::DataLayout::ALL_LAYOUT,
tensor.dtype()};
using kernel_signature = void (*)(
const phi::DeviceContext&, const phi::DenseTensor&, phi::DenseTensor*);
PD_VISIT_KERNEL("contiguous",
kernel_key,
kernel_signature,
false,
*dev_ctx,
tensor,
&dense_out);
return dense_out;
#endif
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Place type is not supported when casting data type."));
}
return tensor;
}
void CheckAndTrans2Contiguous(phi::DenseTensor* tensor) {
if (!tensor->meta().is_contiguous()) {
phi::DenseTensor tmp = Trans2Contiguous(*tensor);
tensor->ShareDataWith(tmp);
}
}
phi::DenseTensor CheckAndTrans2NewContiguousTensor(
const phi::DenseTensor& tensor) {
if (!tensor.meta().is_contiguous()) {
return Trans2Contiguous(tensor);
}
return tensor;
}
std::vector<phi::DenseTensor> CheckAndTrans2NewContiguousTensor(
const std::vector<phi::DenseTensor>& tensor) {
std::vector<phi::DenseTensor> out;
out.reserve(tensor.size());
for (auto& t : tensor) {
out.emplace_back(CheckAndTrans2NewContiguousTensor(t));
}
return out;
}
phi::DenseTensor TransformData(const phi::DenseTensor& tensor,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
phi::DenseTensor out = tensor;
bool trans_layout = false;
bool trans_dtype = false;
if (NeedTransform2Contiguous(is_stride_kernel, out.meta().is_contiguous())) {
out = Trans2Contiguous(out);
}
if (NeedTransformLayout(tensor.layout(),
target_args_def.layout,
tensor.place(),
transform_flag) &&
tensor.dims().size() != 1) {
if (NeedTransform2Contiguous(false, out.meta().is_contiguous())) {
out = Trans2Contiguous(out);
}
out = TransDataLayout(out, target_args_def.layout);
trans_layout = true;
}
if (NeedTransformDataType(
tensor.dtype(), target_args_def.dtype, transform_flag)) {
if (NeedTransform2Contiguous(false, out.meta().is_contiguous())) {
out = Trans2Contiguous(out);
}
out = TransDataType(out, target_args_def.dtype);
trans_dtype = true;
}
if (NeedTransformPlace(
out.place(), target_args_def.backend, transform_flag)) {
out = TransDataPlace(out, phi::TransToPhiPlace(target_args_def.backend));
if (!trans_layout && !trans_dtype &&
tensor.place().GetType() == AllocationType::GPUPINNED) {
// Sharing buffer on GPUPINNED place is a special case due to historical
// reasons, and it should not be implemented in this way from a
// reasonable point of view, but because the performance of the previous
// model depends on the inplace operation here, the model performance
// will deteriorate after reverting to non-place impl, so it needs to be
// retained here and need to use `const_cast`
const_cast<phi::DenseTensor&>(tensor).ShareBufferWith(out);
}
}
return out;
}
std::shared_ptr<phi::DenseTensor> PrepareData(
const Tensor& input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
const auto& tensor_in = input.impl();
if (tensor_in) {
phi::DenseTensor& dense_tensor =
*static_cast<phi::DenseTensor*>(tensor_in.get());
if (!transform_flag.NeedTransform() || !dense_tensor.initialized() ||
(!NeedTransformPlace(
dense_tensor.place(), target_args_def.backend, transform_flag) &&
!NeedTransformDataType(
dense_tensor.dtype(), target_args_def.dtype, transform_flag) &&
!NeedTransformLayout(dense_tensor.layout(),
target_args_def.layout,
dense_tensor.place(),
transform_flag) &&
!NeedTransform2Contiguous(is_stride_kernel,
dense_tensor.meta().is_contiguous()))) {
if (NeedTransform2Contiguous(is_stride_kernel,
dense_tensor.meta().is_contiguous()) &&
dense_tensor.initialized()) {
phi::DenseTensor out = dense_tensor;
out = Trans2Contiguous(out);
return std::make_shared<phi::DenseTensor>(std::move(out));
}
return std::static_pointer_cast<phi::DenseTensor>(tensor_in);
}
phi::DenseTensor out = TransformData(
dense_tensor, target_args_def, transform_flag, is_stride_kernel);
return std::make_shared<phi::DenseTensor>(std::move(out));
}
return nullptr;
}
paddle::optional<phi::DenseTensor> PrepareData(
const paddle::optional<Tensor>& input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
if (input) {
return {*PrepareData(
*input, target_args_def, transform_flag, is_stride_kernel)};
}
return paddle::none;
}
std::unique_ptr<std::vector<phi::DenseTensor>> PrepareData(
const std::vector<Tensor>& inputs,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
auto pt_tensors = std::make_unique<std::vector<phi::DenseTensor>>();
pt_tensors->reserve(inputs.size());
for (const auto& input : inputs) {
const auto& tensor_in = input.impl();
auto dense_tensor = std::dynamic_pointer_cast<phi::DenseTensor>(tensor_in);
if (!transform_flag.NeedTransform() || !tensor_in->initialized() ||
(!NeedTransformPlace(
tensor_in->place(), target_args_def.backend, transform_flag) &&
!NeedTransformDataType(
tensor_in->dtype(), target_args_def.dtype, transform_flag) &&
!NeedTransformLayout(tensor_in->layout(),
target_args_def.layout,
tensor_in->place(),
transform_flag) &&
!(dense_tensor &&
NeedTransform2Contiguous(is_stride_kernel,
dense_tensor->meta().is_contiguous())))) {
if (NeedTransform2Contiguous(is_stride_kernel,
dense_tensor->meta().is_contiguous()) &&
tensor_in->initialized()) {
phi::DenseTensor out =
*(static_cast<phi::DenseTensor*>(tensor_in.get()));
out = Trans2Contiguous(out);
pt_tensors->emplace_back(out);
} else {
pt_tensors->emplace_back(
*std::dynamic_pointer_cast<phi::DenseTensor>(tensor_in));
}
} else {
pt_tensors->emplace_back(
TransformData(*(static_cast<phi::DenseTensor*>(tensor_in.get())),
target_args_def,
transform_flag,
is_stride_kernel));
}
}
return pt_tensors;
}
paddle::optional<std::vector<phi::DenseTensor>> PrepareData(
const paddle::optional<std::vector<Tensor>>& inputs,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
if (inputs) {
return {*PrepareData(
*inputs, target_args_def, transform_flag, is_stride_kernel)};
}
return paddle::none;
}
std::shared_ptr<phi::SelectedRows> PrepareDataForSelectedRows(
const Tensor& input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag) {
const auto& tensor_in = input.impl();
if (tensor_in) {
phi::SelectedRows& selected_rows =
*static_cast<phi::SelectedRows*>(tensor_in.get());
if ((!transform_flag.NeedTransform() || !selected_rows.initialized() ||
(!NeedTransformPlace(selected_rows.place(),
target_args_def.backend,
transform_flag))) &&
!NeedTransform2Contiguous(
false, selected_rows.value().meta().is_contiguous())) {
if (NeedTransform2Contiguous(
false, selected_rows.value().meta().is_contiguous()) &&
selected_rows.initialized()) {
auto out_new = std::make_shared<phi::SelectedRows>(
selected_rows.rows(), selected_rows.height());
auto dense_out = Trans2Contiguous(selected_rows.value());
*out_new->mutable_value() = dense_out;
return out_new;
}
return std::static_pointer_cast<phi::SelectedRows>(tensor_in);
}
if (selected_rows.place().GetType() == AllocationType::GPUPINNED) {
if (NeedTransform2Contiguous(
false, selected_rows.value().meta().is_contiguous())) {
auto dense_out = Trans2Contiguous(selected_rows.value());
selected_rows.mutable_value()->ShareDataWith(dense_out);
}
if (transform_flag.NeedTransform() && selected_rows.initialized() &&
NeedTransformPlace(
selected_rows.place(), target_args_def.backend, transform_flag)) {
auto dense_out =
TransDataPlace(selected_rows.value(),
phi::TransToPhiPlace(target_args_def.backend));
selected_rows.mutable_value()->ShareBufferWith(dense_out);
}
return std::static_pointer_cast<phi::SelectedRows>(tensor_in);
} else {
auto out_new = std::make_shared<phi::SelectedRows>(
selected_rows.rows(), selected_rows.height());
if (NeedTransform2Contiguous(
false, selected_rows.value().meta().is_contiguous())) {
auto dense_out = Trans2Contiguous(selected_rows.value());
*out_new->mutable_value() = dense_out;
}
if (transform_flag.NeedTransform() && selected_rows.initialized() &&
NeedTransformPlace(
selected_rows.place(), target_args_def.backend, transform_flag)) {
auto dense_out =
TransDataPlace(selected_rows.value(),
phi::TransToPhiPlace(target_args_def.backend));
*out_new->mutable_value() = dense_out;
}
return out_new;
}
}
PADDLE_THROW(common::errors::InvalidArgument(
"The impl() of input tensor is nullptr, it doesn't support for "
"selected_rows data transform now."));
}
paddle::optional<phi::SelectedRows> PrepareDataForSelectedRows(
const paddle::optional<Tensor>& input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag) {
if (input) {
return *PrepareDataForSelectedRows(*input, target_args_def, transform_flag);
}
return paddle::none;
}
std::shared_ptr<phi::SparseCooTensor> PrepareDataForSparseCooTensor(
const Tensor& input) {
const auto& tensor_in = input.impl();
if (tensor_in) {
phi::SparseCooTensor& sparse_tensor =
*static_cast<phi::SparseCooTensor*>(tensor_in.get());
if (sparse_tensor.indices().meta().is_contiguous() &&
sparse_tensor.values().meta().is_contiguous()) {
return std::static_pointer_cast<phi::SparseCooTensor>(tensor_in);
}
if (!sparse_tensor.indices().meta().is_contiguous()) {
*sparse_tensor.mutable_indices() =
Trans2Contiguous(sparse_tensor.indices());
}
if (!sparse_tensor.values().meta().is_contiguous()) {
*sparse_tensor.mutable_values() =
Trans2Contiguous(sparse_tensor.values());
}
return std::static_pointer_cast<phi::SparseCooTensor>(tensor_in);
}
PADDLE_THROW(common::errors::InvalidArgument(
"The impl() of input tensor is nullptr, it doesn't support for "
"SparseCooTensor data transform now."));
}
paddle::optional<phi::SparseCooTensor> PrepareDataForSparseCooTensor(
const paddle::optional<Tensor>& input) {
if (input) {
return *PrepareDataForSparseCooTensor(*input);
}
return paddle::none;
}
std::shared_ptr<phi::SparseCsrTensor> PrepareDataForSparseCsrTensor(
const Tensor& input) {
const auto& tensor_in = input.impl();
if (tensor_in) {
phi::SparseCsrTensor& sparse_tensor =
*static_cast<phi::SparseCsrTensor*>(tensor_in.get());
if (sparse_tensor.crows().meta().is_contiguous() &&
sparse_tensor.cols().meta().is_contiguous() &&
sparse_tensor.values().meta().is_contiguous()) {
return std::static_pointer_cast<phi::SparseCsrTensor>(tensor_in);
}
if (!sparse_tensor.crows().meta().is_contiguous()) {
*sparse_tensor.mutable_crows() = Trans2Contiguous(sparse_tensor.crows());
}
if (!sparse_tensor.cols().meta().is_contiguous()) {
*sparse_tensor.mutable_cols() = Trans2Contiguous(sparse_tensor.cols());
}
if (!sparse_tensor.values().meta().is_contiguous()) {
*sparse_tensor.mutable_values() =
Trans2Contiguous(sparse_tensor.values());
}
return std::static_pointer_cast<phi::SparseCsrTensor>(tensor_in);
}
PADDLE_THROW(common::errors::InvalidArgument(
"The impl() of input tensor is nullptr, it doesn't support for "
"SparseCsrTensor data transform now."));
}
paddle::optional<phi::SparseCsrTensor> PrepareDataForSparseCsrTensor(
const paddle::optional<Tensor>& input) {
if (input) {
return *PrepareDataForSparseCsrTensor(*input);
}
return paddle::none;
}
std::shared_ptr<phi::DenseTensor> PrepareDataForDenseTensorInSparse(
const Tensor& input) {
const auto& tensor_in = input.impl();
if (tensor_in) {
phi::DenseTensor& dense_tensor =
*static_cast<phi::DenseTensor*>(tensor_in.get());
if (dense_tensor.meta().is_contiguous()) {
return std::static_pointer_cast<phi::DenseTensor>(tensor_in);
}
return std::make_shared<phi::DenseTensor>(Trans2Contiguous(dense_tensor));
}
PADDLE_THROW(common::errors::InvalidArgument(
"The impl() of input tensor is nullptr, it doesn't support for "
"DenseTensor data transform now."));
}
paddle::optional<phi::DenseTensor> PrepareDataForDenseTensorInSparse(
const paddle::optional<Tensor>& input) {
if (input) {
return *PrepareDataForDenseTensorInSparse(*input);
}
return paddle::none;
}
void TransDataBackend(const phi::DenseTensor* tensor,
Backend target_backend,
phi::DenseTensor* out) {
if (tensor && tensor->initialized()) {
*out = TransDataPlace(*tensor, phi::TransToPhiPlace(target_backend));
}
}
void TransDataBackend(const std::vector<phi::DenseTensor*>& tensors,
Backend target_backend,
std::vector<phi::DenseTensor*> outs) {
size_t n = tensors.size();
for (size_t i = 0; i < n; ++i) {
TransDataBackend(tensors[i], target_backend, outs[i]);
}
}
void TransDataBackend(const phi::SelectedRows* tensor,
Backend target_backend,
phi::SelectedRows* out) {
if (tensor) {
TransDataBackend(&tensor->value(), target_backend, out->mutable_value());
}
}
/* ------------------ for auto parallel ----------------------- */
static bool ReshardIsNeededWithPartial(
const phi::distributed::TensorDistAttr& in_dist_attr,
const phi::distributed::TensorDistAttr& out_dist_attr) {
return (in_dist_attr.process_mesh() != out_dist_attr.process_mesh() ||
in_dist_attr.multi_dims_mapping() !=
out_dist_attr.multi_dims_mapping() ||
in_dist_attr.partial_status() != out_dist_attr.partial_status());
}
static bool ReshardIsNeeded(
const phi::distributed::TensorDistAttr& in_dist_attr,
const phi::distributed::TensorDistAttr& out_dist_attr) {
return (in_dist_attr.process_mesh() != out_dist_attr.process_mesh() ||
in_dist_attr.multi_dims_mapping() !=
out_dist_attr.multi_dims_mapping());
}
std::string ReshardDebugInfo(
const phi::distributed::DistTensor& src_tensor,
const phi::distributed::TensorDistAttr& dist_attr) {
std::stringstream sstream;
sstream << "reshard from {Global Shape: " << src_tensor.dims()
<< ", Local Shape: " << src_tensor.local_dims()
<< ", DistAttr: " << src_tensor.dist_attr()
<< "} to {DistAttr: " << dist_attr << "}";
return sstream.str();
}
std::shared_ptr<phi::distributed::DistTensor> ReshardApiInputToKernelInput(
phi::DeviceContext* dev_ctx,
const Tensor& tensor,
const phi::distributed::ArgDistAttr& dist_attr,
const std::string& arg_name) {
PADDLE_ENFORCE_EQ(
paddle::holds_alternative<phi::distributed::TensorDistAttr>(dist_attr),
true,
common::errors::PreconditionNotMet("Arg must be a TensorDistAttr"));
auto tensor_in = tensor.impl();
const auto& tensor_dist_attr = paddle::get<0>(dist_attr);
if (tensor_in) {
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor_in.get());
if (ReshardIsNeededWithPartial(dist_tensor->dist_attr(),
tensor_dist_attr)) {
auto argument_name = (arg_name.empty() ? "tensor" : arg_name);
auto tensor_name = (tensor.name().empty() ? "None" : tensor.name());
VLOG(4) << "Reshard input: " << argument_name << "(" << tensor_name
<< ") " << ReshardDebugInfo(*dist_tensor, tensor_dist_attr);
auto* func = phi::distributed::ChooseProperReshardFunction(
*dist_tensor, tensor_dist_attr);
return func->Eval(dev_ctx, *dist_tensor, tensor_dist_attr);
}
return std::static_pointer_cast<phi::distributed::DistTensor>(tensor_in);
}
return nullptr;
}
std::vector<std::shared_ptr<phi::distributed::DistTensor>>
ReshardApiInputToKernelInput(phi::DeviceContext* dev_ctx,
const std::vector<Tensor>& tensors,
const phi::distributed::ArgDistAttr& dist_attrs,
const std::string& arg_name) {
PADDLE_ENFORCE_EQ(
paddle::holds_alternative<std::vector<phi::distributed::TensorDistAttr>>(
dist_attrs),
true,
common::errors::PreconditionNotMet(
"Arg must be a vector of TensorDistAttr"));
const auto& tensor_dist_attrs = PADDLE_GET_CONST(
std::vector<phi::distributed::TensorDistAttr>, dist_attrs);
PADDLE_ENFORCE_EQ(tensors.size(),
tensor_dist_attrs.size(),
common::errors::InvalidArgument(
"Tensor's size should be equal to dist_attrs' size."));
std::vector<std::shared_ptr<phi::distributed::DistTensor>> out;
for (size_t i = 0; i < tensors.size(); i++) {
auto tensor_in = tensors[i].impl();
auto dist_attr = tensor_dist_attrs[i];
if (tensor_in) {
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor_in.get());
VLOG(4) << "ReshardIsNeededWithPartial "
<< ReshardIsNeededWithPartial(dist_tensor->dist_attr(),
dist_attr);
if (ReshardIsNeededWithPartial(dist_tensor->dist_attr(), dist_attr)) {
auto argument_name =
(arg_name.empty() ? "tensor" : arg_name) + "_" + std::to_string(i);
auto tensor_name =
(tensors[i].name().empty() ? "None" : tensors[i].name());
VLOG(4) << "Reshard input: " << argument_name << "(" << tensor_name
<< ") " << ReshardDebugInfo(*dist_tensor, dist_attr);
auto* func = phi::distributed::ChooseProperReshardFunction(*dist_tensor,
dist_attr);
out.push_back(func->Eval(dev_ctx, *dist_tensor, dist_attr));
} else {
out.push_back(
std::static_pointer_cast<phi::distributed::DistTensor>(tensor_in));
}
} else {
out.push_back(nullptr);
}
}
return out;
}
paddle::optional<std::shared_ptr<phi::distributed::DistTensor>>
ReshardApiInputToKernelInput(phi::DeviceContext* dev_ctx,
const paddle::optional<Tensor>& tensor,
const phi::distributed::ArgDistAttr& dist_attr,
const std::string& arg_name) {
if (tensor) {
VLOG(6) << "Optional ApiIn to Replicated KernelIn.";
return paddle::make_optional<std::shared_ptr<phi::distributed::DistTensor>>(
ReshardApiInputToKernelInput(dev_ctx, *tensor, dist_attr, arg_name));
}
return paddle::none;
}
paddle::optional<std::vector<std::shared_ptr<phi::distributed::DistTensor>>>
ReshardApiInputToKernelInput(
phi::DeviceContext* dev_ctx,
const paddle::optional<std::vector<Tensor>>& tensors,
const phi::distributed::ArgDistAttr& dist_attrs,
const std::string& arg_name) {
if (tensors) {
VLOG(6) << "Optional ApiIn to Replicated KernelIn.";
return paddle::make_optional<
std::vector<std::shared_ptr<phi::distributed::DistTensor>>>(
ReshardApiInputToKernelInput(dev_ctx, *tensors, dist_attrs, arg_name));
}
return paddle::none;
}
void SetInplaceOutputCorrectDistAttr(
phi::DeviceContext* dev_ctx,
Tensor& tensor, // NOLINT
const phi::distributed::TensorDistAttr& dist_attr,
bool use_general_spmd_rule) {
auto tensor_in = tensor.impl();
if (tensor_in) {
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor_in.get());
if (dist_tensor->initialized()) {
if (use_general_spmd_rule) {
if (ReshardIsNeeded(dist_tensor->dist_attr(), dist_attr)) {
VLOG(6) << "SetInplaceOutputCorrectDistAttr Reshard inplace output"
<< " to origin dist_attr "
<< ReshardDebugInfo(*dist_tensor, dist_attr);
auto* func = phi::distributed::ChooseProperReshardFunction(
*dist_tensor, dist_attr);
func->Eval(dev_ctx, *dist_tensor, dist_attr, dist_tensor);
return;
}
}
}
VLOG(6) << "SetInplaceOutputCorrectDistAttr for tensor " << tensor.name()
<< ", just set its dist_attr from " << dist_tensor->dist_attr()
<< " to " << dist_attr;
dist_tensor->unsafe_set_dist_attr(dist_attr);
}
}
void SetInplaceOutputCorrectDistAttr(
phi::DeviceContext* dev_ctx,
Tensor& tensor, // NOLINT
const phi::distributed::ArgDistAttr& dist_attr,
bool use_general_spmd_rule) {
PADDLE_ENFORCE_EQ(
paddle::holds_alternative<phi::distributed::TensorDistAttr>(dist_attr),
true,
common::errors::PreconditionNotMet("Arg must be a TensorDistAttr"));
SetInplaceOutputCorrectDistAttr(
dev_ctx, tensor, paddle::get<0>(dist_attr), use_general_spmd_rule);
}
void SetInplaceOutputCorrectDistAttr(
phi::DeviceContext* dev_ctx,
std::vector<Tensor>& tensors, // NOLINT
const std::vector<phi::distributed::TensorDistAttr>& dist_attr,
bool use_general_spmd_rule) {
for (size_t i = 0; i < tensors.size(); i++) {
auto tensor_in = tensors[i].impl();
if (tensor_in) {
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor_in.get());
if (dist_tensor->initialized()) {
if (use_general_spmd_rule) {
if (ReshardIsNeededWithPartial(dist_tensor->dist_attr(),
dist_attr[i])) {
VLOG(6) << "SetInplaceOutputCorrectDistAttr Reshard inplace output"
<< " to origin dist_attr "
<< ReshardDebugInfo(*dist_tensor, dist_attr[i]);
auto* func = phi::distributed::ChooseProperReshardFunction(
*dist_tensor, dist_attr[i]);
func->Eval(dev_ctx, *dist_tensor, dist_attr[i], dist_tensor);
continue;
}
}
}
VLOG(6) << "SetInplaceOutputCorrectDistAttr for tensor "
<< tensors[i].name() << ", just set its dist_attr from "
<< dist_tensor->dist_attr() << " to " << dist_attr[i];
dist_tensor->unsafe_set_dist_attr(dist_attr[i]);
}
}
}
void SetInplaceOutputCorrectDistAttr(
phi::DeviceContext* dev_ctx,
std::vector<Tensor>& tensors, // NOLINT
const phi::distributed::ArgDistAttr& dist_attr,
bool use_general_spmd_rule) {
PADDLE_ENFORCE_EQ(
paddle::holds_alternative<std::vector<phi::distributed::TensorDistAttr>>(
dist_attr),
true,
common::errors::PreconditionNotMet(
"Arg must be a vector of TensorDistAttr"));
SetInplaceOutputCorrectDistAttr(
dev_ctx, tensors, paddle::get<1>(dist_attr), use_general_spmd_rule);
}
void ReshardKernelOutputToApiOutput(
phi::DeviceContext* dev_ctx,
const std::shared_ptr<phi::distributed::DistTensor>& src_tensor,
Tensor* dst_tensor,
const std::string& arg_name) {
if (dst_tensor) {
auto tensor_out = dst_tensor->impl();
PADDLE_ENFORCE_NE(
tensor_out,
nullptr,
common::errors::InvalidArgument("The output tensor is nullptr."));
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor_out.get());
dist_tensor->unsafe_set_dims(src_tensor->dims());
// skip_sharding3_output_reshard for sharding stage3 with AMP.
bool skip_sharding3_output_reshard =
std::getenv("skip_sharding3_output_reshard") &&
std::string(std::getenv("skip_sharding3_output_reshard")) == "1" &&
src_tensor->dist_attr().is_partial() &&
dist_tensor->dist_attr().is_shard();
if (!skip_sharding3_output_reshard &&
ReshardIsNeeded(src_tensor->dist_attr(), dist_tensor->dist_attr())) {
auto argument_name = (arg_name.empty() ? "tensor" : arg_name);
auto tensor_name =
(dst_tensor->name().empty() ? "None" : src_tensor->name());
VLOG(4) << "Reshard output(bwd): " << argument_name << "(" << tensor_name
<< ") "
<< ReshardDebugInfo(*src_tensor, dist_tensor->dist_attr());
auto* func = phi::distributed::ChooseProperReshardFunction(
*src_tensor, dist_tensor->dist_attr());
func->Eval(dev_ctx, *src_tensor, dist_tensor->dist_attr(), dist_tensor);
} else {
// TODO(chenweihang): add dist attr compare and default copy rule to
// avoid add branch here
// shallow copy dense tensor
*dist_tensor->unsafe_mutable_value() = src_tensor->value();
dist_tensor->unsafe_set_dist_attr(src_tensor->dist_attr());
}
} else {
VLOG(3) << "The output tensor is nullptr when call "
"ReshardKernelOutputToApiOutput.";
}
}
void ReshardKernelOutputToApiOutput(
phi::DeviceContext* dev_ctx,
const std::vector<std::shared_ptr<phi::distributed::DistTensor>>&
src_tensors,
const std::vector<Tensor*>& dst_tensors,
const std::string& arg_name) {
PADDLE_ENFORCE_EQ(
src_tensors.size(),
dst_tensors.size(),
common::errors::PreconditionNotMet(
"src_tensors.size() [%d] and dst_tensors.size() [%d] not match",
src_tensors.size(),
dst_tensors.size()));
auto size = src_tensors.size();
for (size_t i = 0; i < size; i++) {
ReshardKernelOutputToApiOutput(
dev_ctx, src_tensors[i], dst_tensors[i], arg_name);
}
}
std::shared_ptr<phi::distributed::DistTensor> PrepareDataForDistTensor(
std::shared_ptr<phi::distributed::DistTensor> input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
if (input) {
phi::distributed::DistTensor* dist_tensor = input.get();
const phi::DenseTensor& dense_tensor = dist_tensor->value();
if (!transform_flag.NeedTransform() || !dense_tensor.initialized() ||
(!NeedTransformPlace(
dense_tensor.place(), target_args_def.backend, transform_flag) &&
!NeedTransformDataType(
dense_tensor.dtype(), target_args_def.dtype, transform_flag) &&
!NeedTransformLayout(dense_tensor.layout(),
target_args_def.layout,
dense_tensor.place(),
transform_flag) &&
!NeedTransform2Contiguous(is_stride_kernel,
dense_tensor.meta().is_contiguous()))) {
if (NeedTransform2Contiguous(is_stride_kernel,
dense_tensor.meta().is_contiguous()) &&
dense_tensor.initialized()) {
auto dist_out = std::make_shared<phi::distributed::DistTensor>(
dist_tensor->dims(), dist_tensor->dist_attr());
auto* out = dist_out->unsafe_mutable_value();
*out = Trans2Contiguous(dense_tensor);
return dist_out;
}
return input;
}
// TODO(chenweihang): The global meta in DistTensor is not changed,
// but the local meta in DenseTensor maybe changed, such as layout
// change(NCHW->NHWC), so the new DistTensor's meta maybe not unified.
VLOG(6) << "PrepareDataForDistTensor return transformed dist tensor";
auto dist_out = std::make_shared<phi::distributed::DistTensor>(
dist_tensor->dims(), dist_tensor->dist_attr());
auto* out = dist_out->unsafe_mutable_value();
*out = TransformData(
dense_tensor, target_args_def, transform_flag, is_stride_kernel);
return dist_out;
}
return nullptr;
}
std::vector<std::shared_ptr<phi::distributed::DistTensor>>
PrepareDataForDistTensor(
std::vector<std::shared_ptr<phi::distributed::DistTensor>> input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
std::vector<std::shared_ptr<phi::distributed::DistTensor>> out;
for (const auto& tensor_in : input) {
if (tensor_in) {
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor_in.get());
const phi::DenseTensor& dense_tensor = dist_tensor->value();
if (!transform_flag.NeedTransform() || !dense_tensor.initialized() ||
(!NeedTransformPlace(
dense_tensor.place(), target_args_def.backend, transform_flag) &&
!NeedTransformDataType(
dense_tensor.dtype(), target_args_def.dtype, transform_flag) &&
!NeedTransformLayout(dense_tensor.layout(),
target_args_def.layout,
dense_tensor.place(),
transform_flag) &&
!NeedTransform2Contiguous(is_stride_kernel,
dense_tensor.meta().is_contiguous()))) {
if (NeedTransform2Contiguous(is_stride_kernel,
dense_tensor.meta().is_contiguous()) &&
dense_tensor.initialized()) {
phi::DenseTensor trans_in_tensor = Trans2Contiguous(dense_tensor);
out.push_back(std::make_shared<phi::distributed::DistTensor>(
std::make_shared<phi::DenseTensor>(trans_in_tensor),
dist_tensor->dist_attr()));
} else {
out.push_back(std::static_pointer_cast<phi::distributed::DistTensor>(
tensor_in));
}
} else {
phi::DenseTensor trans_in_tensor = TransformData(
dense_tensor, target_args_def, transform_flag, is_stride_kernel);
// TODO(GhostScreaming): The global meta in DistTensor is not changed,
// but the local meta in DenseTensor maybe changed, such as layout
// change(NCHW->NHWC), so the new DistTensor's meta maybe not unified.
VLOG(6) << "PrepareDataForDistTensor return transformed dist tensor";
out.push_back(std::make_shared<phi::distributed::DistTensor>(
std::make_shared<phi::DenseTensor>(trans_in_tensor),
dist_tensor->dist_attr()));
}
} else {
out.push_back(nullptr);
}
}
return out;
}
paddle::optional<std::shared_ptr<phi::distributed::DistTensor>>
PrepareDataForDistTensor(
paddle::optional<std::shared_ptr<phi::distributed::DistTensor>> input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
if (input) {
VLOG(6) << "PrepareDataForDistTensor for optional return transformed dist "
"tensor";
return paddle::make_optional<std::shared_ptr<phi::distributed::DistTensor>>(
PrepareDataForDistTensor(
*input, target_args_def, transform_flag, is_stride_kernel));
}
return paddle::none;
}
paddle::optional<std::vector<std::shared_ptr<phi::distributed::DistTensor>>>
PrepareDataForDistTensor(
paddle::optional<std::vector<std::shared_ptr<phi::distributed::DistTensor>>>
input,
const phi::TensorArgDef& target_args_def,
const TransformFlag& transform_flag,
bool is_stride_kernel) {
if (input) {
VLOG(6) << "PrepareDataForDistTensor for optional vector return "
"transformed dist "
"tensor";
return paddle::make_optional<
std::vector<std::shared_ptr<phi::distributed::DistTensor>>>(
PrepareDataForDistTensor(
*input, target_args_def, transform_flag, is_stride_kernel));
}
return paddle::none;
}
} // namespace paddle::experimental