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

1525 lines
62 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/api_custom_impl.h"
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/api/lib/tensor_copy.h"
#include "paddle/phi/common/type_traits.h"
#include "paddle/phi/core/compat/convert_utils.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/meta_tensor.h"
#include "paddle/phi/infermeta/backward.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/infermeta/fusion.h"
#include "paddle/phi/infermeta/multiary.h"
#include "paddle/phi/infermeta/nullary.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/api/profiler/event_tracing.h"
#include "paddle/phi/api/profiler/supplement_tracing.h"
#ifdef PADDLE_WITH_DISTRIBUTE
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
#include "paddle/phi/infermeta/spmd_rules/rules.h"
#endif
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
namespace paddle::experimental {
////////////////// Forward api impls //////////////////////
Tensor add_n_impl(const std::vector<Tensor>& x) {
Backend kernel_backend = Backend::UNDEFINED;
DataLayout kernel_layout = DataLayout::UNDEFINED;
DataType kernel_data_type = DataType::UNDEFINED;
if (kernel_backend == Backend::UNDEFINED ||
kernel_layout == DataLayout::UNDEFINED ||
kernel_data_type == DataType::UNDEFINED) {
auto kernel_key_set = ParseKernelKeyByInputArgs(x);
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
if (kernel_backend == Backend::UNDEFINED) {
kernel_backend = kernel_key.backend();
}
if (kernel_layout == DataLayout::UNDEFINED) {
kernel_layout = kernel_key.layout();
}
if (kernel_data_type == DataType::UNDEFINED) {
kernel_data_type = kernel_key.dtype();
}
}
bool is_sr_kernel = true;
for (auto& input : x) {
if (phi::DenseTensor::classof(input.impl().get()) ||
phi::distributed::DistTensor::classof(input.impl().get())) {
is_sr_kernel = false;
break;
}
}
const std::string kernel_name = (is_sr_kernel ? "add_n_sr" : "add_n");
VLOG(6) << "add_n API kernel key: [" << kernel_backend << ", "
<< kernel_layout << ", " << kernel_data_type << "]";
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
kernel_name, {kernel_backend, kernel_layout, kernel_data_type});
const auto& kernel = kernel_result.kernel;
VLOG(6) << kernel_name << " kernel: " << kernel;
auto* dev_ctx = GetDeviceContextByBackend(
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
Tensor api_output;
if (is_sr_kernel) {
std::vector<const phi::SelectedRows*> input_x(x.size());
for (size_t i = 0; i < input_x.size(); ++i) {
input_x[i] = static_cast<phi::SelectedRows*>(x[i].impl().get());
}
auto x_meta_vec = MakeMetaTensor(input_x);
std::vector<const phi::MetaTensor*> x_metas(x_meta_vec.size());
for (size_t i = 0; i < x_meta_vec.size(); ++i) {
x_metas[i] = &x_meta_vec[i];
}
auto kernel_out = SetSelectedRowsKernelOutput(&api_output);
phi::MetaTensor meta_out(kernel_out);
phi::AddNInferMeta(x_metas, &meta_out);
using kernel_signature =
void (*)(const phi::DeviceContext&,
const std::vector<const phi::SelectedRows*>&,
phi::SelectedRows*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx, input_x, kernel_out);
} else {
#ifdef PADDLE_WITH_DISTRIBUTE
bool run_auto_parallel = AllInputsAreDistTensor(x);
bool rank_is_in_current_mesh = true;
if (run_auto_parallel) {
auto mesh =
std::static_pointer_cast<phi::distributed::DistTensor>(x[0].impl())
->dist_attr()
.process_mesh();
rank_is_in_current_mesh = phi::distributed::IsCurRankInMesh(mesh);
std::vector<const phi::TensorBase*> input_x(x.size());
for (size_t i = 0; i < input_x.size(); ++i) {
input_x[i] = x[i].impl().get();
}
auto meta_dist_input_x = MakeDistMetaTensor(input_x);
auto spmd_info = phi::distributed::VariadicReplicatedInferSpmdDynamic(
meta_dist_input_x);
auto dist_out = SetKernelDistOutput(&api_output);
auto dense_out = dist_out->unsafe_mutable_value();
if (!rank_is_in_current_mesh) {
*dense_out = phi::DenseTensor(
std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
phi::MetaTensor meta_dist_out(dist_out);
auto x_meta_vec = MakeMetaTensor(input_x);
std::vector<const phi::MetaTensor*> x_metas(x_meta_vec.size());
for (size_t i = 0; i < x_meta_vec.size(); ++i) {
x_metas[i] = &x_meta_vec[i];
}
phi::AddNInferMeta(x_metas, &meta_dist_out);
if (rank_is_in_current_mesh) {
auto dist_input_x =
ReshardApiInputToKernelInput(dev_ctx, x, spmd_info.first[0]);
dist_input_x = PrepareDataForDistTensor(
dist_input_x,
GetKernelInputArgDef(kernel.InputAt(0), kernel_backend),
{},
kernel_result.is_stride_kernel);
std::vector<const phi::TensorBase*> input_x(dist_input_x.size());
for (size_t i = 0; i < dist_input_x.size(); ++i) {
input_x[i] = dist_input_x[i]->unsafe_mutable_value();
}
auto x_meta_vec = MakeMetaTensor(input_x);
std::vector<const phi::MetaTensor*> x_metas(x_meta_vec.size());
for (size_t i = 0; i < x_meta_vec.size(); ++i) {
x_metas[i] = &x_meta_vec[i];
}
phi::MetaTensor meta_dense_out(dense_out);
phi::AddNInferMeta(x_metas, &meta_dense_out);
using kernel_signature =
void (*)(const phi::DeviceContext&,
const std::vector<const phi::TensorBase*>&,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx, input_x, dense_out);
}
PADDLE_ENFORCE_EQ(paddle::holds_alternative<
std::vector<phi::distributed::TensorDistAttr>>(
spmd_info.first[0]),
true,
common::errors::PreconditionNotMet(
"Arg must be a vector of TensorDistAttr"));
auto current_process_mesh =
paddle::get<1>(spmd_info.first[0]).at(0).process_mesh();
SetReplicatedDistAttrForOutput(dist_out, current_process_mesh);
return api_output;
}
#endif
std::vector<const phi::TensorBase*> input_x(x.size());
std::vector<std::shared_ptr<phi::DenseTensor>> temp_dense_tensors;
temp_dense_tensors.reserve(x.size());
for (size_t i = 0; i < input_x.size(); ++i) {
if (phi::DenseTensor::classof(x[i].impl().get())) {
temp_dense_tensors.push_back(
PrepareData(x[i], kernel.InputAt(0), {}, false));
input_x[i] = temp_dense_tensors.back().get();
} else {
input_x[i] = x[i].impl().get();
}
}
auto x_meta_vec = MakeMetaTensor(input_x);
std::vector<const phi::MetaTensor*> x_metas(x_meta_vec.size());
for (size_t i = 0; i < x_meta_vec.size(); ++i) {
x_metas[i] = &x_meta_vec[i];
}
auto kernel_out = SetKernelOutput(&api_output);
phi::MetaTensor meta_out(kernel_out);
std::vector<phi::MetaTensor*> output_metas_for_compact;
output_metas_for_compact.push_back(&meta_out);
phi::AddNInferMeta(x_metas, &meta_out);
CheckAndDoCompact(output_metas_for_compact, "add_n");
using kernel_signature =
void (*)(const phi::DeviceContext&,
const std::vector<const phi::TensorBase*>&,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx, input_x, kernel_out);
if (kernel_result.has_fallback_cpu) {
TransDataBackend(kernel_out, kernel_backend, kernel_out);
}
}
return api_output;
}
Tensor copy_to_impl(const Tensor& x, Place place, bool blocking) {
Tensor out;
copy(x, place, blocking, &out);
out.set_name(x.name());
return out;
}
std::tuple<Tensor, Tensor> fused_gemm_epilogue_impl(
const Tensor& x,
const Tensor& y,
const Tensor& bias,
bool trans_x,
bool trans_y,
const std::string& activation) {
// Kernel Key Construction
Backend kernel_backend = Backend::UNDEFINED;
DataLayout kernel_layout = DataLayout::UNDEFINED;
DataType kernel_data_type = DataType::UNDEFINED;
if (kernel_backend == Backend::UNDEFINED ||
kernel_layout == DataLayout::UNDEFINED ||
kernel_data_type == DataType::UNDEFINED) {
auto kernel_key_set = ParseKernelKeyByInputArgs(x, y, bias);
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
if (kernel_backend == Backend::UNDEFINED) {
kernel_backend = kernel_key.backend();
}
if (kernel_layout == DataLayout::UNDEFINED) {
kernel_layout = kernel_key.layout();
}
if (kernel_data_type == DataType::UNDEFINED) {
kernel_data_type = kernel_key.dtype();
}
}
#ifdef PADDLE_WITH_DISTRIBUTE
bool run_auto_parallel = AllInputsAreDistTensor(x, y, bias);
bool rank_is_in_current_mesh = true;
if (run_auto_parallel) {
auto mesh =
std::static_pointer_cast<phi::distributed::DistTensor>(bias.impl())
->dist_attr()
.process_mesh();
rank_is_in_current_mesh = phi::distributed::IsCurRankInMesh(mesh);
}
// Kernel Dispatch Body
// Auto Parallel condition
if (run_auto_parallel) {
// 1. InferSpmd (Infer DistAttr of Inputs&Outputs)
auto meta_dist_input_x = MakeDistMetaTensor(*x.impl());
auto meta_dist_input_y = MakeDistMetaTensor(*y.impl());
auto meta_dist_input_bias = MakeDistMetaTensor(*bias.impl());
auto spmd_info =
phi::distributed::FusedGemmEpilogueInferSpmd(meta_dist_input_x,
meta_dist_input_y,
meta_dist_input_bias,
trans_x,
trans_y,
activation);
DebugInfoForInferSpmd("fused_gemm_epilogue", spmd_info);
// 2. Create API Output & Prepare Dist and Dense Output
phi::DeviceContext* dev_ctx = nullptr;
std::tuple<Tensor, Tensor> api_output;
auto dist_out_0 =
SetKernelDistOutput(&std::get<0>(api_output), spmd_info.second[0]);
auto dense_out_0 =
dist_out_0 ? dist_out_0->unsafe_mutable_value() : nullptr;
if (!rank_is_in_current_mesh) {
*dense_out_0 =
phi::DenseTensor(std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
phi::distributed::DistTensor* dist_out_1 = nullptr;
if (activation != "none") {
dist_out_1 =
SetKernelDistOutput(&std::get<1>(api_output), spmd_info.second[1]);
}
auto dense_out_1 =
dist_out_1 ? dist_out_1->unsafe_mutable_value() : nullptr;
if (!rank_is_in_current_mesh) {
*dense_out_1 =
phi::DenseTensor(std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
// 3. Infer DistTensor's Global Shape
phi::MetaTensor meta_dist_out_0(dist_out_0);
phi::MetaTensor meta_dist_out_1(dist_out_1);
phi::FusedGemmEpilogueInferMeta(MakeMetaTensor(*x.impl()),
MakeMetaTensor(*y.impl()),
MakeMetaTensor(*bias.impl()),
trans_x,
trans_y,
activation,
dist_out_0 ? &meta_dist_out_0 : nullptr,
dist_out_1 ? &meta_dist_out_1 : nullptr);
if (rank_is_in_current_mesh) {
// 4. Select Kernel
VLOG(6) << "fused_gemm_epilogue API dist branch: kernel key: ["
<< kernel_backend << ", " << kernel_layout << ", "
<< kernel_data_type << "]";
auto kernel_result =
phi::KernelFactory::Instance().SelectKernelOrThrowError(
"fused_gemm_epilogue",
{kernel_backend, kernel_layout, kernel_data_type});
const auto& kernel = kernel_result.kernel;
VLOG(6) << "fused_gemm_epilogue kernel: " << kernel;
dev_ctx = GetDeviceContextByBackend(
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
// 5. Reshard Input
auto dist_input_x =
ReshardApiInputToKernelInput(dev_ctx, x, spmd_info.first[0], "x");
auto dist_input_y =
ReshardApiInputToKernelInput(dev_ctx, y, spmd_info.first[1], "y");
auto dist_input_bias = ReshardApiInputToKernelInput(
dev_ctx, bias, spmd_info.first[2], "bias");
// 6. PrepareData (DataTransform & Prepare Dense Input)
dist_input_x = PrepareDataForDistTensor(
dist_input_x,
GetKernelInputArgDef(kernel.InputAt(0), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_x = &dist_input_x->value();
dist_input_y = PrepareDataForDistTensor(
dist_input_y,
GetKernelInputArgDef(kernel.InputAt(1), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_y = &dist_input_y->value();
dist_input_bias = PrepareDataForDistTensor(
dist_input_bias,
GetKernelInputArgDef(kernel.InputAt(2), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_bias = &dist_input_bias->value();
// 7. RecordOpInfoSupplement
if (phi::RecordOpInfoSupplement::IsEnabled()) {
std::vector<std::pair<const char*, std::vector<phi::DDim>>>
input_shapes{{"x", {(*input_x).dims()}},
{"y", {(*input_y).dims()}},
{"bias", {(*input_bias).dims()}}};
phi::AttributeMap attrs;
attrs["trans_x"] = trans_x;
attrs["trans_y"] = trans_y;
attrs["activation"] = activation;
phi::RecordOpInfoSupplement("fused_gemm_epilogue", input_shapes, attrs);
}
// 8. Infer Local DenseTensor Meta
phi::MetaTensor meta_dense_out_0(dense_out_0);
phi::MetaTensor meta_dense_out_1(dense_out_1);
phi::FusedGemmEpilogueInferMeta(
MakeMetaTensor(*input_x),
MakeMetaTensor(*input_y),
MakeMetaTensor(*input_bias),
trans_x,
trans_y,
activation,
dense_out_0 ? &meta_dense_out_0 : nullptr,
dense_out_1 ? &meta_dense_out_1 : nullptr);
// 9. DenseTensor Kernel Call
phi::RecordEvent* kernel_record_event = nullptr;
if (phi::RecordEvent::IsEnabled()) {
kernel_record_event =
new phi::RecordEvent("fused_gemm_epilogue dist compute",
phi::TracerEventType::DygraphKernelLaunch,
1);
}
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
bool,
bool,
const std::string&,
phi::DenseTensor*,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_y,
*input_bias,
trans_x,
trans_y,
activation,
dense_out_0,
dense_out_1);
if (FLAGS_benchmark) {
dev_ctx->Wait();
std::cout << "fused_gemm_epilogue kernel run finish." << std::endl;
}
if (kernel_record_event != nullptr) {
delete kernel_record_event;
}
// 10. Fallback
if (kernel_result.has_fallback_cpu) {
TransDataBackend(dense_out_0, kernel_backend, dense_out_0);
TransDataBackend(dense_out_1, kernel_backend, dense_out_1);
}
}
// 11. Set Output Dist Attr For Default Impl
// API `fused_gemm_epilogue` does not need to set DistAttr for output.
// 12. Return
return api_output;
}
#endif // PADDLE_WITH_DISTRIBUTE
VLOG(6) << "fused_gemm_epilogue API kernel key: [" << kernel_backend << ", "
<< kernel_layout << ", " << kernel_data_type << "]";
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
"fused_gemm_epilogue",
{kernel_backend, kernel_layout, kernel_data_type},
true);
const auto& kernel = kernel_result.kernel;
if (FLAGS_low_precision_op_list) {
phi::KernelFactory::Instance().AddToLowPrecisionKernelList(
"fused_gemm_epilogue", kernel_data_type);
}
VLOG(6) << "fused_gemm_epilogue kernel: " << kernel;
// add actual_kernel_backend to select actual kernel backend after a potential
// falling-back to CPU
Backend actual_kernel_backend =
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend;
auto* dev_ctx = GetDeviceContextByBackend(actual_kernel_backend);
auto input_x = PrepareData(
x,
GetKernelInputArgDef(kernel.InputAt(0), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_y = PrepareData(
y,
GetKernelInputArgDef(kernel.InputAt(1), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_bias = PrepareData(
bias,
GetKernelInputArgDef(kernel.InputAt(2), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
if (phi::RecordOpInfoSupplement::IsEnabled()) {
std::vector<std::pair<const char*, std::vector<phi::DDim>>> input_shapes{
{"x", {(*input_x).dims()}},
{"y", {(*input_y).dims()}},
{"bias", {(*input_bias).dims()}}};
phi::AttributeMap attrs;
attrs["trans_x"] = trans_x;
attrs["trans_y"] = trans_y;
attrs["activation"] = activation;
phi::RecordOpInfoSupplement("fused_gemm_epilogue", input_shapes, attrs);
}
std::tuple<Tensor, Tensor> api_output;
auto kernel_out_0 = SetKernelOutput(&std::get<0>(api_output));
phi::DenseTensor* kernel_out_1 = nullptr;
if (activation != "none") {
kernel_out_1 = SetKernelOutput(&std::get<1>(api_output));
}
phi::RecordEvent* infer_shape_record_event = nullptr;
if (phi::RecordEvent::IsEnabled()) {
infer_shape_record_event =
new phi::RecordEvent("fused_gemm_epilogue infer_meta",
phi::TracerEventType::OperatorInner,
1);
}
phi::MetaTensor meta_out_0(kernel_out_0, kernel_result.is_stride_kernel);
phi::MetaTensor meta_out_1(kernel_out_1, kernel_result.is_stride_kernel);
std::vector<phi::MetaTensor*> output_metas_for_compact;
if (kernel_out_0) output_metas_for_compact.push_back(&meta_out_0);
if (kernel_out_1) output_metas_for_compact.push_back(&meta_out_1);
phi::FusedGemmEpilogueInferMeta(MakeMetaTensor(*input_x),
MakeMetaTensor(*input_y),
MakeMetaTensor(*input_bias),
trans_x,
trans_y,
activation,
kernel_out_0 ? &meta_out_0 : nullptr,
kernel_out_1 ? &meta_out_1 : nullptr);
CheckAndDoCompact(output_metas_for_compact, "fused_gemm_epilogue");
if (infer_shape_record_event != nullptr) {
delete infer_shape_record_event;
}
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
bool,
bool,
const std::string&,
phi::DenseTensor*,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
phi::RecordEvent* kernel_record_event = nullptr;
if (phi::RecordEvent::IsEnabled()) {
kernel_record_event =
new phi::RecordEvent("fused_gemm_epilogue kernel launch",
phi::TracerEventType::DygraphKernelLaunch,
1);
}
(*kernel_fn)(*dev_ctx,
*input_x,
*input_y,
*input_bias,
trans_x,
trans_y,
activation,
kernel_out_0,
kernel_out_1);
if (FLAGS_benchmark) {
dev_ctx->Wait();
std::cout << "fused_gemm_epilogue kernel run finish." << std::endl;
}
if (kernel_record_event != nullptr) {
delete kernel_record_event;
}
if (kernel_result.has_fallback_cpu) {
TransDataBackend(kernel_out_0, kernel_backend, kernel_out_0);
TransDataBackend(kernel_out_1, kernel_backend, kernel_out_1);
}
return api_output;
}
// weight_list.size() should be weight_list.get_ptr()->size() but can't modify
// yaml file
std::tuple<Tensor, Tensor, Tensor, std::vector<Tensor>> cudnn_lstm_grad_impl(
const Tensor& x,
const Tensor& init_h,
const Tensor& init_c,
const paddle::optional<std::vector<Tensor>>& weight_list,
const paddle::optional<Tensor>& sequence_length,
const Tensor& out,
const Tensor& reserve,
const Tensor& state_out,
const Tensor& out_grad,
const Tensor& last_h_grad,
const Tensor& last_c_grad,
float dropout_prob,
bool is_bidirec,
int hidden_size,
int num_layers,
bool is_test,
int seed) {
// Kernel Key Construction
Backend kernel_backend = Backend::UNDEFINED;
DataLayout kernel_layout = DataLayout::UNDEFINED;
DataType kernel_data_type = DataType::UNDEFINED;
#ifdef PADDLE_WITH_DISTRIBUTE
bool run_auto_parallel = AllInputsAreDistTensor(x,
init_h,
init_c,
weight_list,
sequence_length,
out,
reserve,
state_out,
out_grad,
last_h_grad,
last_c_grad);
bool rank_is_in_current_mesh = true;
if (run_auto_parallel) {
auto mesh = std::static_pointer_cast<phi::distributed::DistTensor>(
last_c_grad.impl())
->dist_attr()
.process_mesh();
rank_is_in_current_mesh = phi::distributed::IsCurRankInMesh(mesh);
}
if (rank_is_in_current_mesh) {
kernel_data_type = ParseDataType(out_grad);
if (kernel_backend == Backend::UNDEFINED ||
kernel_layout == DataLayout::UNDEFINED ||
kernel_data_type == DataType::UNDEFINED) {
auto kernel_key_set = ParseKernelKeyByInputArgs(x,
init_h,
init_c,
weight_list,
sequence_length,
out,
reserve,
state_out,
out_grad,
last_h_grad,
last_c_grad);
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
if (kernel_backend == Backend::UNDEFINED) {
kernel_backend = kernel_key.backend();
}
if (kernel_layout == DataLayout::UNDEFINED) {
kernel_layout = kernel_key.layout();
}
if (kernel_data_type == DataType::UNDEFINED) {
kernel_data_type = kernel_key.dtype();
}
}
}
// Kernel Dispatch Body
// Auto Parallel condition
if (run_auto_parallel) {
// 1. InferSpmd (Infer DistAttr of Inputs&Outputs)
auto meta_dist_input_x = MakeDistMetaTensor(*x.impl());
auto meta_dist_input_init_h = MakeDistMetaTensor(*init_h.impl());
auto meta_dist_input_init_c = MakeDistMetaTensor(*init_c.impl());
std::vector<phi::distributed::DistMetaTensor> meta_dist_input_weight_list;
if (weight_list) {
for (auto& e : *weight_list) {
meta_dist_input_weight_list.push_back(MakeDistMetaTensor(*e.impl()));
}
}
auto meta_dist_input_sequence_length =
sequence_length ? MakeDistMetaTensor(*(*sequence_length).impl())
: phi::distributed::DistMetaTensor();
auto meta_dist_input_out = MakeDistMetaTensor(*out.impl());
auto meta_dist_input_reserve = MakeDistMetaTensor(*reserve.impl());
auto meta_dist_input_state_out = MakeDistMetaTensor(*state_out.impl());
auto meta_dist_input_out_grad = MakeDistMetaTensor(*out_grad.impl());
auto meta_dist_input_last_h_grad = MakeDistMetaTensor(*last_h_grad.impl());
auto meta_dist_input_last_c_grad = MakeDistMetaTensor(*last_c_grad.impl());
auto spmd_info = phi::distributed::VariadicReplicatedInferSpmdDynamic(
meta_dist_input_x,
meta_dist_input_init_h,
meta_dist_input_init_c,
meta_dist_input_weight_list,
meta_dist_input_sequence_length,
meta_dist_input_out,
meta_dist_input_reserve,
meta_dist_input_state_out,
meta_dist_input_out_grad,
meta_dist_input_last_h_grad,
meta_dist_input_last_c_grad);
DebugInfoForInferSpmd("cudnn_lstm_grad", spmd_info);
// 2. Create API Output & Prepare Dist and Dense Output
phi::DeviceContext* dev_ctx = nullptr;
std::tuple<Tensor, Tensor, Tensor, std::vector<Tensor>> api_output;
auto dist_out_0 = SetKernelDistOutput(&std::get<0>(api_output));
auto dense_out_0 =
dist_out_0 ? dist_out_0->unsafe_mutable_value() : nullptr;
if (!rank_is_in_current_mesh) {
*dense_out_0 =
phi::DenseTensor(std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
auto dist_out_1 = SetKernelDistOutput(&std::get<1>(api_output));
auto dense_out_1 =
dist_out_1 ? dist_out_1->unsafe_mutable_value() : nullptr;
if (!rank_is_in_current_mesh) {
*dense_out_1 =
phi::DenseTensor(std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
auto dist_out_2 = SetKernelDistOutput(&std::get<2>(api_output));
auto dense_out_2 =
dist_out_2 ? dist_out_2->unsafe_mutable_value() : nullptr;
if (!rank_is_in_current_mesh) {
*dense_out_2 =
phi::DenseTensor(std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
auto dist_out_3 = SetKernelDistOutput(weight_list.get_ptr()->size(),
&std::get<3>(api_output));
std::vector<phi::DenseTensor*> dense_out_3(dist_out_3.size());
for (size_t i = 0; i < dist_out_3.size(); ++i) {
dense_out_3[i] = const_cast<phi::DenseTensor*>(&dist_out_3[i]->value());
if (!rank_is_in_current_mesh) {
*dense_out_3[i] = phi::DenseTensor(
std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
}
// 3. Infer DistTensor's Global Shape
phi::MetaTensor meta_dist_out_0(dist_out_0);
phi::MetaTensor meta_dist_out_1(dist_out_1);
phi::MetaTensor meta_dist_out_2(dist_out_2);
std::vector<phi::MetaTensor> dist_out_3_meta_vec;
for (phi::distributed::DistTensor* tmp : dist_out_3) {
dist_out_3_meta_vec.emplace_back(phi::MetaTensor(tmp));
}
std::vector<phi::MetaTensor*> dist_out_3_meta_ptr_vec(dist_out_3.size());
for (size_t i = 0; i < dist_out_3_meta_vec.size(); ++i) {
dist_out_3_meta_ptr_vec[i] =
dist_out_3[i] ? &dist_out_3_meta_vec[i] : nullptr;
}
std::vector<phi::MetaTensor> weight_list_meta_vec_tmp;
if (weight_list) {
for (auto tmp : *weight_list) {
weight_list_meta_vec_tmp.emplace_back(MakeMetaTensor(*tmp.impl()));
}
}
std::vector<const phi::MetaTensor*> weight_list_meta_ptr_vec_tmp(
weight_list_meta_vec_tmp.size());
for (size_t i = 0; i < weight_list_meta_ptr_vec_tmp.size(); ++i) {
weight_list_meta_ptr_vec_tmp[i] = &weight_list_meta_vec_tmp[i];
}
paddle::optional<std::vector<const phi::MetaTensor*>>
weight_list_meta_ptr_vec =
weight_list
? paddle::make_optional<std::vector<const phi::MetaTensor*>>(
weight_list_meta_ptr_vec_tmp)
: paddle::none;
phi::CudnnLSTMGradInferMeta(MakeMetaTensor(*x.impl()),
MakeMetaTensor(*init_h.impl()),
MakeMetaTensor(*init_c.impl()),
weight_list_meta_ptr_vec,
dist_out_0 ? &meta_dist_out_0 : nullptr,
dist_out_1 ? &meta_dist_out_1 : nullptr,
dist_out_2 ? &meta_dist_out_2 : nullptr,
dist_out_3_meta_ptr_vec);
if (rank_is_in_current_mesh) {
// 4. Select Kernel
VLOG(6) << "cudnn_lstm_grad API dist branch: kernel key: ["
<< kernel_backend << ", " << kernel_layout << ", "
<< kernel_data_type << "]";
auto kernel_result =
phi::KernelFactory::Instance().SelectKernelOrThrowError(
"cudnn_lstm_grad",
{kernel_backend, kernel_layout, kernel_data_type});
const auto& kernel = kernel_result.kernel;
VLOG(6) << "cudnn_lstm_grad kernel: " << kernel;
dev_ctx = GetDeviceContextByBackend(
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
// 5. Reshard Input
auto dist_input_x =
ReshardApiInputToKernelInput(dev_ctx, x, spmd_info.first[0], "x");
auto dist_input_init_h = ReshardApiInputToKernelInput(
dev_ctx, init_h, spmd_info.first[1], "init_h");
auto dist_input_init_c = ReshardApiInputToKernelInput(
dev_ctx, init_c, spmd_info.first[2], "init_c");
auto dist_input_weight_list = ReshardApiInputToKernelInput(
dev_ctx, weight_list, spmd_info.first[3], "weight_list");
auto dist_input_sequence_length = ReshardApiInputToKernelInput(
dev_ctx, sequence_length, spmd_info.first[4], "sequence_length");
auto dist_input_out =
ReshardApiInputToKernelInput(dev_ctx, out, spmd_info.first[5], "out");
auto dist_input_reserve = ReshardApiInputToKernelInput(
dev_ctx, reserve, spmd_info.first[6], "reserve");
auto dist_input_state_out = ReshardApiInputToKernelInput(
dev_ctx, state_out, spmd_info.first[7], "state_out");
auto dist_input_out_grad = ReshardApiInputToKernelInput(
dev_ctx, out_grad, spmd_info.first[8], "out_grad");
auto dist_input_last_h_grad = ReshardApiInputToKernelInput(
dev_ctx, last_h_grad, spmd_info.first[9], "last_h_grad");
auto dist_input_last_c_grad = ReshardApiInputToKernelInput(
dev_ctx, last_c_grad, spmd_info.first[10], "last_c_grad");
// 6. PrepareData (DataTransform & Prepare Dense Input)
dist_input_x = PrepareDataForDistTensor(
dist_input_x,
GetKernelInputArgDef(kernel.InputAt(0), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_x = &dist_input_x->value();
dist_input_init_h = PrepareDataForDistTensor(
dist_input_init_h,
GetKernelInputArgDef(kernel.InputAt(1), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_init_h = &dist_input_init_h->value();
dist_input_init_c = PrepareDataForDistTensor(
dist_input_init_c,
GetKernelInputArgDef(kernel.InputAt(2), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_init_c = &dist_input_init_c->value();
auto dist_input_weight_list_vec = PrepareDataForDistTensor(
dist_input_weight_list,
GetKernelInputArgDef(kernel.InputAt(3), kernel_backend),
{},
kernel_result.is_stride_kernel);
std::vector<const phi::DenseTensor*> dense_input_weight_list_vec;
if (weight_list) {
for (auto tmp : *dist_input_weight_list_vec) {
dense_input_weight_list_vec.emplace_back(&tmp->value());
}
}
paddle::optional<std::vector<const phi::DenseTensor*>> input_weight_list(
dense_input_weight_list_vec);
std::vector<phi::MetaTensor> dense_input_weight_list_meta_vec =
MakeMetaTensor(dense_input_weight_list_vec);
std::vector<const phi::MetaTensor*>
dense_input_weight_list_meta_ptr_vec_tmp(
dense_input_weight_list_meta_vec.size());
for (size_t i = 0; i < dense_input_weight_list_meta_ptr_vec_tmp.size();
++i) {
dense_input_weight_list_meta_ptr_vec_tmp[i] =
&dense_input_weight_list_meta_vec[i];
}
paddle::optional<std::vector<const phi::MetaTensor*>>
dense_input_weight_list_meta_ptr_vec =
weight_list
? paddle::make_optional<std::vector<const phi::MetaTensor*>>(
dense_input_weight_list_meta_ptr_vec_tmp)
: paddle::none;
dist_input_sequence_length = PrepareDataForDistTensor(
dist_input_sequence_length,
GetKernelInputArgDef(kernel.InputAt(4), kernel_backend),
{},
kernel_result.is_stride_kernel);
paddle::optional<phi::DenseTensor> input_sequence_length =
dist_input_sequence_length
? paddle::make_optional<phi::DenseTensor>(
(*dist_input_sequence_length)->value())
: paddle::none;
dist_input_out = PrepareDataForDistTensor(
dist_input_out,
GetKernelInputArgDef(kernel.InputAt(5), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_out = &dist_input_out->value();
dist_input_reserve = PrepareDataForDistTensor(
dist_input_reserve,
GetKernelInputArgDef(kernel.InputAt(6), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_reserve = &dist_input_reserve->value();
dist_input_state_out = PrepareDataForDistTensor(
dist_input_state_out,
GetKernelInputArgDef(kernel.InputAt(7), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_state_out = &dist_input_state_out->value();
dist_input_out_grad = PrepareDataForDistTensor(
dist_input_out_grad,
GetKernelInputArgDef(kernel.InputAt(8), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_out_grad = &dist_input_out_grad->value();
dist_input_last_h_grad = PrepareDataForDistTensor(
dist_input_last_h_grad,
GetKernelInputArgDef(kernel.InputAt(9), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_last_h_grad = &dist_input_last_h_grad->value();
dist_input_last_c_grad = PrepareDataForDistTensor(
dist_input_last_c_grad,
GetKernelInputArgDef(kernel.InputAt(10), kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_last_c_grad = &dist_input_last_c_grad->value();
// 7. RecordOpInfoSupplement
if (phi::RecordOpInfoSupplement::IsEnabled()) {
std::vector<phi::DDim> sequence_length_record_shapes;
if (input_sequence_length) {
sequence_length_record_shapes.push_back(
(*input_sequence_length).dims());
}
std::vector<std::pair<const char*, std::vector<phi::DDim>>>
input_shapes{{"x", {(*input_x).dims()}},
{"init_h", {(*input_init_h).dims()}},
{"init_c", {(*input_init_c).dims()}},
{"sequence_length", sequence_length_record_shapes},
{"out", {(*input_out).dims()}},
{"reserve", {(*input_reserve).dims()}},
{"state_out", {(*input_state_out).dims()}},
{"out_grad", {(*input_out_grad).dims()}},
{"last_h_grad", {(*input_last_h_grad).dims()}},
{"last_c_grad", {(*input_last_c_grad).dims()}}};
std::vector<phi::DDim> ddims_vec;
ddims_vec.clear();
if (input_weight_list) {
ddims_vec.reserve(input_weight_list->size());
for (size_t i = 0; i < input_weight_list->size(); ++i) {
ddims_vec.emplace_back((*input_weight_list->at(i)).dims());
}
}
input_shapes.emplace_back("weight_list", ddims_vec);
phi::AttributeMap attrs;
attrs["dropout_prob"] = dropout_prob;
attrs["is_bidirec"] = is_bidirec;
attrs["hidden_size"] = hidden_size;
attrs["num_layers"] = num_layers;
attrs["is_test"] = is_test;
attrs["seed"] = seed;
phi::RecordOpInfoSupplement("cudnn_lstm_grad", input_shapes, attrs);
}
// 8. Infer Local DenseTensor Meta
phi::MetaTensor meta_dense_out_0(dense_out_0);
phi::MetaTensor meta_dense_out_1(dense_out_1);
phi::MetaTensor meta_dense_out_2(dense_out_2);
std::vector<phi::MetaTensor> dense_out_3_meta_vec =
MakeMetaTensor(dense_out_3);
std::vector<phi::MetaTensor*> dense_out_3_meta_ptr_vec(
dense_out_3_meta_vec.size());
for (size_t i = 0; i < dense_out_3_meta_vec.size(); ++i) {
dense_out_3_meta_ptr_vec[i] =
dense_out_3[i] ? &dense_out_3_meta_vec[i] : nullptr;
}
phi::CudnnLSTMGradInferMeta(MakeMetaTensor(*input_x),
MakeMetaTensor(*input_init_h),
MakeMetaTensor(*input_init_c),
dense_input_weight_list_meta_ptr_vec,
dense_out_0 ? &meta_dense_out_0 : nullptr,
dense_out_1 ? &meta_dense_out_1 : nullptr,
dense_out_2 ? &meta_dense_out_2 : nullptr,
dense_out_3_meta_ptr_vec);
// 9. DenseTensor Kernel Call
phi::RecordEvent* kernel_record_event = nullptr;
if (phi::RecordEvent::IsEnabled()) {
kernel_record_event =
new phi::RecordEvent("cudnn_lstm_grad dist compute",
phi::TracerEventType::DygraphKernelLaunch,
1);
}
using kernel_signature = void (*)(
const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const paddle::optional<std::vector<const phi::DenseTensor*>>&,
const paddle::optional<phi::DenseTensor>&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
float,
bool,
int,
int,
bool,
int,
phi::DenseTensor*,
phi::DenseTensor*,
phi::DenseTensor*,
std::vector<phi::DenseTensor*>);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_init_h,
*input_init_c,
input_weight_list,
input_sequence_length,
*input_out,
*input_reserve,
*input_state_out,
*input_out_grad,
*input_last_h_grad,
*input_last_c_grad,
dropout_prob,
is_bidirec,
hidden_size,
num_layers,
is_test,
seed,
dense_out_0,
dense_out_1,
dense_out_2,
dense_out_3);
if (FLAGS_benchmark) {
dev_ctx->Wait();
std::cout << "cudnn_lstm_grad kernel run finish." << std::endl;
}
if (kernel_record_event != nullptr) {
delete kernel_record_event;
}
// 10. Fallback
if (kernel_result.has_fallback_cpu) {
TransDataBackend(dense_out_0, kernel_backend, dense_out_0);
TransDataBackend(dense_out_1, kernel_backend, dense_out_1);
TransDataBackend(dense_out_2, kernel_backend, dense_out_2);
TransDataBackend(dense_out_3, kernel_backend, dense_out_3);
}
}
// 11. Set Output Dist Attr For Default Impl
auto current_process_mesh =
paddle::holds_alternative<phi::distributed::TensorDistAttr>(
spmd_info.first[0])
? paddle::get<0>(spmd_info.first[0]).process_mesh()
: paddle::get<1>(spmd_info.first[0]).at(0).process_mesh();
SetReplicatedDistAttrForOutput(dist_out_0, current_process_mesh);
SetReplicatedDistAttrForOutput(dist_out_1, current_process_mesh);
SetReplicatedDistAttrForOutput(dist_out_2, current_process_mesh);
for (size_t i = 0; i < dist_out_3.size(); ++i) {
SetReplicatedDistAttrForOutput(dist_out_3[i], current_process_mesh);
}
// 12. Return
return api_output;
}
#endif // PADDLE_WITH_DISTRIBUTE
VLOG(6) << "cudnn_lstm_grad API kernel key: [" << kernel_backend << ", "
<< kernel_layout << ", " << kernel_data_type << "]";
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
"cudnn_lstm_grad",
{kernel_backend, kernel_layout, kernel_data_type},
true);
const auto& kernel = kernel_result.kernel;
if (FLAGS_low_precision_op_list) {
phi::KernelFactory::Instance().AddToLowPrecisionKernelList(
"cudnn_lstm_grad", kernel_data_type);
}
VLOG(6) << "cudnn_lstm_grad kernel: " << kernel;
// add actual_kernel_backend to select actual kernel backend after a potential
// falling-back to CPU
Backend actual_kernel_backend =
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend;
auto* dev_ctx = GetDeviceContextByBackend(actual_kernel_backend);
auto input_x = PrepareData(
x,
GetKernelInputArgDef(kernel.InputAt(0), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_init_h = PrepareData(
init_h,
GetKernelInputArgDef(kernel.InputAt(1), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_init_c = PrepareData(
init_c,
GetKernelInputArgDef(kernel.InputAt(2), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_weight_list_vec = PrepareData(
weight_list,
GetKernelInputArgDef(kernel.InputAt(3), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
paddle::optional<std::vector<const phi::DenseTensor*>> input_weight_list;
if (input_weight_list_vec) {
input_weight_list = paddle::optional<std::vector<const phi::DenseTensor*>>(
input_weight_list_vec->size());
for (size_t i = 0; i < input_weight_list_vec->size(); ++i) {
input_weight_list->at(i) = &input_weight_list_vec->at(i);
}
}
auto input_sequence_length = PrepareData(
sequence_length,
GetKernelInputArgDef(kernel.InputAt(4), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_out = PrepareData(
out,
GetKernelInputArgDef(kernel.InputAt(5), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_reserve = PrepareData(
reserve,
GetKernelInputArgDef(kernel.InputAt(6), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_state_out = PrepareData(
state_out,
GetKernelInputArgDef(kernel.InputAt(7), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_out_grad = PrepareData(
out_grad,
GetKernelInputArgDef(kernel.InputAt(8), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_last_h_grad = PrepareData(
last_h_grad,
GetKernelInputArgDef(kernel.InputAt(9), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
auto input_last_c_grad = PrepareData(
last_c_grad,
GetKernelInputArgDef(kernel.InputAt(10), actual_kernel_backend),
{},
kernel_result.is_stride_kernel);
if (phi::RecordOpInfoSupplement::IsEnabled()) {
std::vector<phi::DDim> sequence_length_record_shapes;
if (input_sequence_length) {
sequence_length_record_shapes.push_back((*input_sequence_length).dims());
}
std::vector<std::pair<const char*, std::vector<phi::DDim>>> input_shapes{
{"x", {(*input_x).dims()}},
{"init_h", {(*input_init_h).dims()}},
{"init_c", {(*input_init_c).dims()}},
{"sequence_length", sequence_length_record_shapes},
{"out", {(*input_out).dims()}},
{"reserve", {(*input_reserve).dims()}},
{"state_out", {(*input_state_out).dims()}},
{"out_grad", {(*input_out_grad).dims()}},
{"last_h_grad", {(*input_last_h_grad).dims()}},
{"last_c_grad", {(*input_last_c_grad).dims()}}};
std::vector<phi::DDim> ddims_vec;
ddims_vec.clear();
if (input_weight_list) {
ddims_vec.reserve(input_weight_list->size());
for (size_t i = 0; i < input_weight_list->size(); ++i) {
ddims_vec.emplace_back((*input_weight_list->at(i)).dims());
}
}
input_shapes.emplace_back("weight_list", ddims_vec);
phi::AttributeMap attrs;
attrs["dropout_prob"] = dropout_prob;
attrs["is_bidirec"] = is_bidirec;
attrs["hidden_size"] = hidden_size;
attrs["num_layers"] = num_layers;
attrs["is_test"] = is_test;
attrs["seed"] = seed;
phi::RecordOpInfoSupplement("cudnn_lstm_grad", input_shapes, attrs);
}
std::tuple<Tensor, Tensor, Tensor, std::vector<Tensor>> api_output;
auto kernel_out_0 = SetKernelOutput(&std::get<0>(api_output));
auto kernel_out_1 = SetKernelOutput(&std::get<1>(api_output));
auto kernel_out_2 = SetKernelOutput(&std::get<2>(api_output));
auto kernel_out_3 =
SetKernelOutput(weight_list.get_ptr()->size(), &std::get<3>(api_output));
phi::RecordEvent* infer_shape_record_event = nullptr;
if (phi::RecordEvent::IsEnabled()) {
infer_shape_record_event = new phi::RecordEvent(
"cudnn_lstm_grad infer_meta", phi::TracerEventType::OperatorInner, 1);
}
auto weight_list_meta_vec = MakeMetaTensor(input_weight_list);
paddle::optional<std::vector<const phi::MetaTensor*>> weight_list_metas(
weight_list_meta_vec.size());
for (size_t i = 0; i < weight_list_meta_vec.size(); ++i) {
weight_list_metas->at(i) = &weight_list_meta_vec[i];
}
phi::MetaTensor meta_out_0(kernel_out_0, kernel_result.is_stride_kernel);
phi::MetaTensor meta_out_1(kernel_out_1, kernel_result.is_stride_kernel);
phi::MetaTensor meta_out_2(kernel_out_2, kernel_result.is_stride_kernel);
auto kernel_out_3_meta_vec = MakeMetaTensor(kernel_out_3);
std::vector<phi::MetaTensor*> kernel_out_3_metas(
kernel_out_3_meta_vec.size());
for (size_t i = 0; i < kernel_out_3_meta_vec.size(); ++i) {
kernel_out_3_metas[i] =
kernel_out_3[i] ? &kernel_out_3_meta_vec[i] : nullptr;
}
std::vector<phi::MetaTensor*> output_metas_for_compact;
if (kernel_out_0) output_metas_for_compact.push_back(&meta_out_0);
if (kernel_out_1) output_metas_for_compact.push_back(&meta_out_1);
if (kernel_out_2) output_metas_for_compact.push_back(&meta_out_1);
output_metas_for_compact.insert(output_metas_for_compact.end(),
kernel_out_3_metas.begin(),
kernel_out_3_metas.end());
phi::CudnnLSTMGradInferMeta(MakeMetaTensor(*input_x),
MakeMetaTensor(*input_init_h),
MakeMetaTensor(*input_init_c),
weight_list_metas,
kernel_out_0 ? &meta_out_0 : nullptr,
kernel_out_1 ? &meta_out_1 : nullptr,
kernel_out_2 ? &meta_out_2 : nullptr,
kernel_out_3_metas);
CheckAndDoCompact(output_metas_for_compact, "cudnn_lstm_grad");
if (infer_shape_record_event != nullptr) {
delete infer_shape_record_event;
}
using kernel_signature =
void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const paddle::optional<std::vector<const phi::DenseTensor*>>&,
const paddle::optional<phi::DenseTensor>&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
float,
bool,
int,
int,
bool,
int,
phi::DenseTensor*,
phi::DenseTensor*,
phi::DenseTensor*,
std::vector<phi::DenseTensor*>);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
phi::RecordEvent* kernel_record_event = nullptr;
if (phi::RecordEvent::IsEnabled()) {
kernel_record_event =
new phi::RecordEvent("cudnn_lstm_grad kernel launch",
phi::TracerEventType::DygraphKernelLaunch,
1);
}
(*kernel_fn)(*dev_ctx,
*input_x,
*input_init_h,
*input_init_c,
input_weight_list,
input_sequence_length,
*input_out,
*input_reserve,
*input_state_out,
*input_out_grad,
*input_last_h_grad,
*input_last_c_grad,
dropout_prob,
is_bidirec,
hidden_size,
num_layers,
is_test,
seed,
kernel_out_0,
kernel_out_1,
kernel_out_2,
kernel_out_3);
if (FLAGS_benchmark) {
dev_ctx->Wait();
std::cout << "cudnn_lstm_grad kernel run finish." << std::endl;
}
if (kernel_record_event != nullptr) {
delete kernel_record_event;
}
if (kernel_result.has_fallback_cpu) {
TransDataBackend(kernel_out_0, kernel_backend, kernel_out_0);
TransDataBackend(kernel_out_1, kernel_backend, kernel_out_1);
TransDataBackend(kernel_out_2, kernel_backend, kernel_out_2);
TransDataBackend(kernel_out_3, kernel_backend, kernel_out_3);
}
return api_output;
}
////////////////// Backward(grad) api impls //////////////////////
void embedding_grad_impl(const Tensor& x,
const Tensor& weight,
const Tensor& out_grad,
int64_t padding_idx,
bool sparse,
Tensor* weight_grad) {
DataType kernel_data_type = ParseDataType(weight);
auto kernel_key_set = ParseKernelKeyByInputArgs(weight);
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
VLOG(6) << "embedding_grad API kernel key: [" << kernel_key.backend() << ", "
<< kernel_key.layout() << ", " << kernel_data_type << "]";
if (phi::DenseTensor::classof(weight.impl().get()) ||
phi::distributed::DistTensor::classof(weight.impl().get())) {
std::string kernel_name =
sparse ? "embedding_sparse_grad" : "embedding_grad";
auto kernel_result =
phi::KernelFactory::Instance().SelectKernelOrThrowError(
kernel_name,
{kernel_key.backend(), kernel_key.layout(), kernel_data_type});
const auto& kernel = kernel_result.kernel;
VLOG(6) << kernel_name << " API kernel: " << kernel;
auto* dev_ctx = GetDeviceContextByBackend(
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_key.backend());
#ifdef PADDLE_WITH_DISTRIBUTE
bool run_auto_parallel = AllInputsAreDistTensor(x, weight, out_grad);
// Auto Parallel condition
if (run_auto_parallel) {
bool rank_is_in_current_mesh = true;
auto mesh =
std::static_pointer_cast<phi::distributed::DistTensor>(x.impl())
->dist_attr()
.process_mesh();
rank_is_in_current_mesh = phi::distributed::IsCurRankInMesh(mesh);
// 1. InferSpmd (Infer DistAttr of Inputs&Outputs)
auto meta_dist_input_x = MakeDistMetaTensor(*x.impl());
auto meta_dist_input_weight = MakeDistMetaTensor(*weight.impl());
auto meta_dist_input_out_grad = MakeDistMetaTensor(*out_grad.impl());
auto spmd_info =
phi::distributed::EmbeddingGradInferSpmd(meta_dist_input_x,
meta_dist_input_weight,
meta_dist_input_out_grad,
padding_idx,
sparse);
// 2. Create Temporary Output & Prepare Dist and Dense Output
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out =
CreateKernelDistOutput(
weight_grad, !rank_is_in_current_mesh, spmd_info.second[0]);
phi::distributed::DistTensor* dist_out = shared_dist_out.get();
phi::DenseTensor* dense_out = dist_out->unsafe_mutable_value();
if (dense_out && !rank_is_in_current_mesh && !dist_out->defined()) {
*dense_out = phi::DenseTensor(
std::make_shared<phi::Allocation>(
nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}
// 3. Infer DistTensor's Global Shape
phi::MetaTensor meta_dist_out(dist_out);
UnchangedInferMeta(MakeMetaTensor(*weight.impl()), &meta_dist_out);
// 4. Set Output Dist Attr For Default Impl
if (rank_is_in_current_mesh) {
// 5. Reshard Input
auto dist_input_x =
ReshardApiInputToKernelInput(dev_ctx, x, spmd_info.first[0]);
auto dist_input_weight =
ReshardApiInputToKernelInput(dev_ctx, weight, spmd_info.first[1]);
auto dist_input_out_grad =
ReshardApiInputToKernelInput(dev_ctx, out_grad, spmd_info.first[2]);
// 6. PrepareData (DataTransform & Prepare Dense Input)
dist_input_x = PrepareDataForDistTensor(
dist_input_x,
GetKernelInputArgDef(kernel.InputAt(0), kernel_key.backend()),
{},
kernel_result.is_stride_kernel);
auto input_x = &dist_input_x->value();
dist_input_weight = PrepareDataForDistTensor(
dist_input_weight,
GetKernelInputArgDef(kernel.InputAt(1), kernel_key.backend()),
{},
kernel_result.is_stride_kernel);
auto input_weight = &dist_input_weight->value();
dist_input_out_grad = PrepareDataForDistTensor(
dist_input_out_grad,
GetKernelInputArgDef(kernel.InputAt(2), kernel_key.backend()),
{},
kernel_result.is_stride_kernel);
auto input_out_grad = &dist_input_out_grad->value();
// 7. Infer Local DenseTensor Meta
phi::MetaTensor meta_dense_out(dense_out);
phi::EmbeddingGradInferMeta(MakeMetaTensor(*input_x),
MakeMetaTensor(*input_weight),
&meta_dense_out);
// 8. DenseTensor Kernel Call
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
int64_t,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_weight,
*input_out_grad,
padding_idx,
dense_out);
}
// 9. Reshard Kernel Output to API output
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out, weight_grad);
// 10. Return
return;
}
#endif // PADDLE_WITH_DISTRIBUTE
auto input_x = PrepareData(x, kernel.InputAt(0), {}, false);
auto input_weight = PrepareData(weight, kernel.InputAt(1), {}, false);
auto input_out_grad = PrepareData(out_grad, kernel.InputAt(2), {}, false);
if (sparse) {
auto* kernel_out = SetSelectedRowsKernelOutput(weight_grad);
phi::MetaTensor meta_out(kernel_out);
meta_out.set_dims(input_weight->dims());
meta_out.set_dtype(input_weight->dtype());
kernel_out->set_height(input_weight->dims()[0]);
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
int64_t,
phi::SelectedRows*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_weight,
*input_out_grad,
padding_idx,
kernel_out);
} else {
auto* kernel_out = SetKernelOutput(weight_grad);
phi::MetaTensor meta_out(kernel_out);
std::vector<phi::MetaTensor*> output_metas_for_compact;
output_metas_for_compact.push_back(&meta_out);
phi::UnchangedInferMeta(MakeMetaTensor(*input_weight), &meta_out);
CheckAndDoCompact(output_metas_for_compact, "embedding_grad");
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::DenseTensor&,
const phi::DenseTensor&,
int64_t,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_weight,
*input_out_grad,
padding_idx,
kernel_out);
}
} else {
std::string kernel_name = sparse ? "sparse_weight_embedding_sparse_grad"
: "sparse_weight_embedding_grad";
auto kernel_result =
phi::KernelFactory::Instance().SelectKernelOrThrowError(
kernel_name,
{kernel_key.backend(), kernel_key.layout(), kernel_data_type});
const auto& kernel = kernel_result.kernel;
VLOG(6) << kernel_name << " API kernel: " << kernel;
auto* dev_ctx = GetDeviceContextByBackend(
kernel_result.has_fallback_cpu ? Backend::CPU : kernel_key.backend());
auto input_x = PrepareData(x, kernel.InputAt(0), {}, false);
auto input_weight = TensorToSelectedRows(weight);
auto input_out_grad = PrepareData(out_grad, kernel.InputAt(2), {}, false);
if (sparse) {
auto* kernel_out = SetSelectedRowsKernelOutput(weight_grad);
phi::MetaTensor meta_out(kernel_out);
phi::UnchangedInferMeta(MakeMetaTensor(*input_weight), &meta_out);
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::SelectedRows&,
const phi::DenseTensor&,
int64_t,
phi::SelectedRows*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_weight,
*input_out_grad,
padding_idx,
kernel_out);
} else {
auto* kernel_out = SetKernelOutput(weight_grad);
phi::MetaTensor meta_out(kernel_out);
meta_out.set_dims(input_weight->GetCompleteDims());
meta_out.set_dtype(input_weight->dtype());
using kernel_signature = void (*)(const phi::DeviceContext&,
const phi::DenseTensor&,
const phi::SelectedRows&,
const phi::DenseTensor&,
int64_t,
phi::DenseTensor*);
auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx,
*input_x,
*input_weight,
*input_out_grad,
padding_idx,
kernel_out);
}
}
}
} // namespace paddle::experimental