Files
paddlepaddle--paddle/paddle/fluid/framework/operator.cc
T
2026-07-13 12:40:42 +08:00

3718 lines
142 KiB
C++

/* Copyright (c) 2016 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/fluid/framework/operator.h"
#include <glog/logging.h>
#include <sstream>
#include <string>
#include <unordered_set>
#include "paddle/common/ddim.h"
#include "paddle/common/flags.h"
#include "paddle/fluid/framework/convert_utils.h"
#include "paddle/fluid/framework/data_transform.h"
#include "paddle/fluid/framework/data_type_transform.h"
#include "paddle/fluid/framework/details/nan_inf_utils.h"
#include "paddle/fluid/framework/op_call_stack.h"
#include "paddle/fluid/framework/phi_utils.h"
#include "paddle/fluid/framework/transfer_scope_cache.h"
#include "paddle/fluid/framework/unused_var_check.h"
#include "paddle/fluid/framework/var_type.h"
#include "paddle/fluid/operators/isfinite_op.h"
#include "paddle/fluid/operators/ops_extra_info.h"
#include "paddle/fluid/operators/ops_signature/signatures.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/fluid/platform/onednn_helper.h"
#include "paddle/fluid/platform/profiler/supplement_tracing.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/core/compat/get_kerneltype_forvar_utils.h"
#include "paddle/phi/core/kernel_context.h"
#include "paddle/phi/core/kernel_factory.h"
#include "paddle/phi/core/platform/device/device_wrapper.h"
#include "paddle/phi/core/platform/onednn_op_list.h"
#include "paddle/phi/core/platform/profiler.h"
#include "paddle/phi/core/platform/profiler/event_tracing.h"
#include "paddle/phi/core/raw_tensor.h"
namespace phi {
class DenseTensor;
} // namespace phi
#ifdef PADDLE_WITH_XPU
#include "paddle/phi/core/platform/device/xpu/xpu_info.h"
#include "paddle/phi/core/platform/device/xpu/xpu_op_list.h"
#endif
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include "paddle/phi/core/platform/device/gpu/gpu_dnn.h"
#endif
COMMON_DECLARE_bool(benchmark);
COMMON_DECLARE_bool(check_nan_inf);
COMMON_DECLARE_bool(run_kp_kernel);
COMMON_DECLARE_bool(enable_host_event_recorder_hook);
namespace paddle::framework {
std::vector<std::tuple<Place, LibraryType>> kKernelPriority = {
std::make_tuple(GPUPlace(0), LibraryType::kCUDNN),
std::make_tuple(GPUPlace(0), LibraryType::kPlain),
std::make_tuple(CPUPlace(), LibraryType::kMKLDNN),
std::make_tuple(CPUPlace(), LibraryType::kPlain),
};
TEST_API paddle::flat_hash_map<std::string, OperatorWithKernel::OpKernelMap>&
OperatorWithKernel::AllOpKernels() {
static paddle::flat_hash_map<std::string, OpKernelMap> g_all_op_kernels;
return g_all_op_kernels;
}
static DDim GetDimsDebug(const Scope& scope,
const std::string& name,
bool get_actual_dim = false) {
Variable* var = scope.FindVar(name);
if (var == nullptr) {
return DDim({-1});
}
if (var->IsType<DenseTensor>()) {
const DenseTensor& tensor = var->Get<DenseTensor>();
return tensor.dims();
} else if (var->IsType<phi::SelectedRows>()) {
if (get_actual_dim) {
return var->Get<phi::SelectedRows>().value().dims();
} else {
return var->Get<phi::SelectedRows>().GetCompleteDims();
}
} else if (var->IsType<Strings>()) {
return DDim({static_cast<int64_t>(var->Get<Strings>().size())});
} else if (var->IsType<phi::SparseCooTensor>()) {
const phi::SparseCooTensor& tensor = var->Get<phi::SparseCooTensor>();
return tensor.dims();
} else if (var->IsType<phi::SparseCsrTensor>()) {
const phi::SparseCsrTensor& tensor = var->Get<phi::SparseCsrTensor>();
return tensor.dims();
} else {
return DDim({-1});
}
}
static bool VarInited(const Scope& scope, const std::string& name) {
Variable* var = scope.FindVar(name);
if (var == nullptr) return false;
return var->IsInitialized();
}
static std::string GetDtype(const Scope& scope, const std::string& name) {
Variable* var = scope.FindVar(name);
if (var == nullptr) {
return "";
}
if (var->IsType<DenseTensor>()) {
const DenseTensor& tensor = var->Get<DenseTensor>();
if (UNLIKELY(!tensor.IsInitialized())) {
return "";
}
return DataTypeToString(framework::TransToProtoVarType(tensor.dtype()));
} else if (var->IsType<phi::SelectedRows>()) {
auto tensor = var->Get<phi::SelectedRows>().value();
if (UNLIKELY(!tensor.IsInitialized())) {
return "uninited";
} else {
return DataTypeToString(framework::TransToProtoVarType(tensor.dtype()));
}
} else if (var->IsType<Strings>()) {
return "strings";
} else if (var->IsType<phi::SparseCooTensor>()) {
const phi::SparseCooTensor& tensor = var->Get<phi::SparseCooTensor>();
if (UNLIKELY(!tensor.has_allocation())) {
return "";
}
return DataTypeToString(framework::TransToProtoVarType(tensor.dtype()));
} else if (var->IsType<phi::SparseCsrTensor>()) {
const phi::SparseCsrTensor& tensor = var->Get<phi::SparseCsrTensor>();
if (UNLIKELY(!tensor.has_allocation())) {
return "";
}
return DataTypeToString(framework::TransToProtoVarType(tensor.dtype()));
} else {
return "";
}
}
static std::string GetPlace(const Scope& scope, const std::string& name) {
Variable* var = scope.FindVar(name);
if (var == nullptr) {
return "";
}
auto to_string = [](const Place& p) {
std::stringstream sstream;
sstream << p;
return sstream.str();
};
if (var->IsType<DenseTensor>()) {
const DenseTensor& tensor = var->Get<DenseTensor>();
if (UNLIKELY(!tensor.IsInitialized())) {
return "";
}
return to_string(tensor.place());
} else if (var->IsType<phi::SelectedRows>()) {
auto tensor = var->Get<phi::SelectedRows>().value();
if (UNLIKELY(!tensor.IsInitialized())) {
return "uninited";
} else {
return to_string(tensor.place());
}
} else {
return "";
}
}
static int GetRowSize(const Scope& scope, const std::string& name) {
Variable* var = scope.FindVar(name);
if (var == nullptr) {
return -1;
}
if (var->IsType<phi::SelectedRows>()) {
return static_cast<int>(var->Get<phi::SelectedRows>().rows().size());
}
return -1;
}
static LegacyLoD GetLoDDebug(const Scope& scope, const std::string& name) {
Variable* var = scope.FindVar(name);
auto default_lod = LegacyLoD({{}});
if (var == nullptr) {
return default_lod;
}
if (var->IsType<DenseTensor>()) {
const DenseTensor& tensor = var->Get<DenseTensor>();
return tensor.lod();
} else {
return default_lod;
}
}
RuntimeContext::RuntimeContext(const VariableNameMap& innames,
const VariableNameMap& outnames,
const Scope& scope) {
for (auto& var_name_item : innames) {
std::vector<Variable*>& input_vars = inputs[var_name_item.first];
input_vars.reserve(var_name_item.second.size());
for (auto& var_name : var_name_item.second) {
input_vars.push_back(scope.FindVar(var_name));
}
}
for (auto& var_name_item : outnames) {
std::vector<Variable*>& output_vars = outputs[var_name_item.first];
output_vars.reserve(var_name_item.second.size());
for (auto& var_name : var_name_item.second) {
output_vars.push_back(scope.FindVar(var_name));
}
}
}
RuntimeInferShapeContext::RuntimeInferShapeContext(const OperatorBase& op,
const RuntimeContext& ctx)
: op_(op), ctx_(ctx) {}
bool RuntimeInferShapeContext::HasInput(const std::string& name) const {
// has only one input
const auto& ins = ctx_.inputs;
auto it = ins.find(name);
if (it == ins.end()) {
return false;
}
const auto& in = it->second;
if (in.empty()) return false;
PADDLE_ENFORCE_EQ(
in.size(),
1UL,
common::errors::InvalidArgument(
"Input %s should not contain more than one inputs.", name));
return in[0] != nullptr;
}
bool RuntimeInferShapeContext::HasOutput(const std::string& name) const {
// has only one output
const auto& outs = ctx_.outputs;
auto it = outs.find(name);
if (it == outs.end()) {
return false;
}
const auto& out = it->second;
if (out.empty()) {
return false;
}
PADDLE_ENFORCE_EQ(
out.size(),
1UL,
common::errors::InvalidArgument(
"Output %s should not contain more than one outputs.", name));
return out[0] != nullptr;
}
bool RuntimeInferShapeContext::HasAttr(const std::string& name) const {
return op_.HasAttr(name);
}
bool RuntimeInferShapeContext::HasInputs(const std::string& name) const {
const auto& ins = ctx_.inputs;
auto it = ins.find(name);
if (it == ins.end() || it->second.empty()) {
return false;
}
for (auto& input : it->second) {
if (input == nullptr) {
return false;
}
}
return true;
}
bool RuntimeInferShapeContext::HasOutputs(const std::string& name,
bool allow_null) const {
const auto& outs = ctx_.outputs;
auto it = outs.find(name);
if (it == outs.end() || it->second.empty()) {
return false;
}
if (!allow_null) {
for (auto& output : it->second) {
if (output == nullptr) return false;
}
}
return true;
}
AttrReader RuntimeInferShapeContext::Attrs() const {
return AttrReader(op_.Attrs(), op_.RuntimeAttrs());
}
std::vector<std::string> RuntimeInferShapeContext::Inputs(
const std::string& name) const {
return op_.Inputs(name);
}
std::vector<std::string> RuntimeInferShapeContext::Outputs(
const std::string& name) const {
return op_.Outputs(name);
}
std::string RuntimeInferShapeContext::GetInputNameByIdx(size_t idx) const {
auto& op_proto =
paddle::framework::OpInfoMap::Instance().Get(op_.Type()).proto_;
PADDLE_ENFORCE_LT(idx,
op_proto->inputs().size(),
common::errors::OutOfRange(
"The index should be less than the size of inputs of "
"operator %s, but got index is %d and size is %d",
op_.Type(),
idx,
op_proto->inputs().size()));
return op_proto->inputs()[static_cast<int>(idx)].name();
}
std::string RuntimeInferShapeContext::GetOutputNameByIdx(size_t idx) const {
auto& op_proto =
paddle::framework::OpInfoMap::Instance().Get(op_.Type()).proto_;
PADDLE_ENFORCE_LT(idx,
op_proto->outputs().size(),
common::errors::OutOfRange(
"The index should be less than the size of outputs of "
"operator %s, but got index is %d and size is %d",
op_.Type(),
idx,
op_proto->outputs().size()));
return op_proto->outputs()[static_cast<int>(idx)].name();
}
void RuntimeInferShapeContext::ShareDim(const std::string& in,
const std::string& out,
size_t i,
size_t j) {
auto in_it = ctx_.inputs.find(in);
auto out_it = ctx_.outputs.find(out);
PADDLE_ENFORCE_NE(in_it,
ctx_.inputs.end(),
common::errors::NotFound("Input %s does not exist.", in));
PADDLE_ENFORCE_NE(out_it,
ctx_.outputs.end(),
common::errors::NotFound("Output %s does not exist.", out));
PADDLE_ENFORCE_LT(i,
in_it->second.size(),
common::errors::InvalidArgument(
"The index of input dimension is out of range, "
"excepted index less than %zu, but received %zu.",
in_it->second.size(),
i));
PADDLE_ENFORCE_LT(j,
out_it->second.size(),
common::errors::InvalidArgument(
"The index of output dimension is out of range, "
"excepted index less than %zu, but received %zu.",
out_it->second.size(),
j));
Variable* in_var = in_it->second[i];
Variable* out_var = out_it->second[j];
PADDLE_ENFORCE_EQ(
in_var->Type(),
out_var->Type(),
common::errors::InvalidArgument(
"The type of input (%s) and output (%s) are inconsistent.", in, out));
if (in_var->IsType<phi::SelectedRows>()) {
auto& in_sele_rows = in_var->Get<phi::SelectedRows>();
auto out_sele_rows = out_var->GetMutable<phi::SelectedRows>();
out_sele_rows->mutable_value()->Resize(in_sele_rows.value().dims());
out_sele_rows->set_rows(in_sele_rows.rows());
out_sele_rows->set_height(in_sele_rows.height());
} else if (in_var->IsType<DenseTensor>()) {
auto& in_lod_tensor = in_var->Get<DenseTensor>();
auto* out_lod_tensor = out_var->GetMutable<DenseTensor>();
out_lod_tensor->Resize(in_lod_tensor.dims());
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Currently, the input type of ShareDim only can be DenseTensor "
"or SelectedRows."));
}
}
void RuntimeInferShapeContext::ShareAllLoD(const std::string& in,
const std::string& out) const {
auto in_it = ctx_.inputs.find(in);
auto out_it = ctx_.outputs.find(out);
PADDLE_ENFORCE_NE(in_it,
ctx_.inputs.end(),
common::errors::NotFound(
"Input [%s] found error in Op [%s]", in, op_.Type()));
PADDLE_ENFORCE_NE(out_it,
ctx_.outputs.end(),
common::errors::NotFound(
"Output [%s] found error in Op [%s]", out, op_.Type()));
auto& in_var_list = in_it->second;
auto& out_var_list = out_it->second;
PADDLE_ENFORCE_EQ(
in_var_list.size(),
out_var_list.size(),
common::errors::PreconditionNotMet(
"Op [%s]: Input var size should be equal with output var size",
op_.Type()));
auto& out_var_names = op_.Outputs(out);
for (size_t i = 0; i < in_var_list.size(); ++i) {
if (out_var_names[i] == framework::kEmptyVarName) {
continue;
}
Variable* in_var = in_var_list[i];
if (!in_var->IsType<DenseTensor>()) return;
Variable* out_var = out_var_list[i];
PADDLE_ENFORCE_EQ(out_var->IsType<DenseTensor>(),
true,
common::errors::PreconditionNotMet(
"The %d-th output of Output(%s) must be DenseTensor.",
i,
out_var_names[i]));
auto& in_tensor = in_var->Get<DenseTensor>();
auto* out_tensor = out_var->GetMutable<DenseTensor>();
out_tensor->set_lod(in_tensor.lod());
#ifdef PADDLE_WITH_DNNL
if (in_tensor.layout() != DataLayout::ONEDNN)
#endif
out_tensor->set_layout(in_tensor.layout());
}
}
void RuntimeInferShapeContext::ShareLoD(const std::string& in,
const std::string& out,
size_t i,
size_t j) const {
if (can_skip_lod_) {
return;
}
auto in_it = ctx_.inputs.find(in);
auto out_it = ctx_.outputs.find(out);
PADDLE_ENFORCE_NE(in_it,
ctx_.inputs.end(),
common::errors::NotFound("Input %s does not exist.", in));
PADDLE_ENFORCE_NE(out_it,
ctx_.outputs.end(),
common::errors::NotFound("Output %s does not exist.", out));
PADDLE_ENFORCE_LT(i,
in_it->second.size(),
common::errors::InvalidArgument(
"The index of input dimension is out of range, "
"excepted index less than %zu, but received %zu.",
in_it->second.size(),
i));
PADDLE_ENFORCE_LT(j,
out_it->second.size(),
common::errors::InvalidArgument(
"The index of output dimension is out of range, "
"excepted index less than %zu, but received %zu.",
out_it->second.size(),
j));
Variable* in_var = in_it->second.at(i);
if (!in_var->IsType<DenseTensor>()) return;
Variable* out_var = out_it->second.at(j);
PADDLE_ENFORCE_EQ(
out_var->IsType<DenseTensor>(),
true,
common::errors::InvalidArgument(
"The %zu-th output of Output(%s) must be DenseTensor.", j, out));
auto& in_tensor = in_var->Get<DenseTensor>();
auto* out_tensor = out_var->GetMutable<DenseTensor>();
out_tensor->set_lod(in_tensor.lod());
// TODO(dzhwinter) : reuse ShareLoD in most operators.
// Need to call ShareLayout explicitly in sequence related ops.
// Shall we have a better method to shared info between in/out DenseTensor?
#ifdef PADDLE_WITH_DNNL
// Fix me: ugly workaround below
// Correct solution:
// set_layout() should NOT be called here (i.e. ShareLoD). Instead,
// layout of output tensor should be set "manually" in Compute()
// of each OPKernel. The reason layout should NOT be shared between
// input and output "automatically" (now by InferShape()->ShareLoD())
// is that layout transform may occur after InferShape().
// Workaround:
// Skip set_layout() when input layout is kMKLDNN
// This is to avoid kMKLDNN is populated wrongly into a non-MKLDNN
// OPKernel. In all OneDNN OPkernel, set_layout(kMKLDNN) should be called
// in Compute()
if (in_tensor.layout() != DataLayout::ONEDNN)
#endif
out_tensor->set_layout(in_tensor.layout());
}
int32_t RuntimeInferShapeContext::GetLoDLevel(const std::string& in,
size_t i) const {
PADDLE_THROW(common::errors::PreconditionNotMet(
"GetLoDLevel is only used in compile time. The calculation of "
"output's actual lod is different among operators so that should be "
"set in the runtime kernel."));
}
void RuntimeInferShapeContext::SetLoDLevel(const std::string& out,
int32_t lod_level,
size_t j) const {
PADDLE_THROW(common::errors::PreconditionNotMet(
"SetLoDLevel is only used in compile time. The calculation of "
"output's actual lod is different among operators so that should be "
"set in the runtime kernel."));
}
bool RuntimeInferShapeContext::IsRuntime() const { return true; }
bool RuntimeInferShapeContext::IsRunONEDNNKernel() const {
try {
auto& op_with_kernel = dynamic_cast<const OperatorWithKernel&>(op_);
return ((op_with_kernel.kernel_type()) &&
(op_with_kernel.kernel_type()->data_layout_ ==
phi::DataLayout::ONEDNN));
} catch (std::bad_cast& exp) {
return false;
}
}
// TODO(paddle-dev): Can this be template?
paddle::small_vector<InferShapeVarPtr, phi::kInputSmallVectorSize>
RuntimeInferShapeContext::GetInputVarPtrs(const std::string& name) const {
const std::vector<Variable*>& vars = InputVars(name);
paddle::small_vector<InferShapeVarPtr, phi::kInputSmallVectorSize> res;
res.reserve(vars.size());
res.insert(res.begin(), vars.begin(), vars.end());
return res;
}
paddle::small_vector<InferShapeVarPtr, phi::kOutputSmallVectorSize>
RuntimeInferShapeContext::GetOutputVarPtrs(const std::string& name) const {
const std::vector<Variable*>& vars = OutputVars(name);
paddle::small_vector<InferShapeVarPtr, phi::kOutputSmallVectorSize> res;
res.reserve(vars.size());
res.insert(res.begin(), vars.begin(), vars.end());
return res;
}
DDim RuntimeInferShapeContext::GetInputDim(const std::string& name) const {
const std::vector<Variable*>& vars = InputVars(name);
PADDLE_ENFORCE_EQ(
vars.size(),
1UL,
common::errors::InvalidArgument(
"Input(%s) should hold one element, but now it holds %zu elements.",
name,
vars.size()));
return this->GetDim(vars[0]);
}
std::vector<DDim> RuntimeInferShapeContext::GetInputsDim(
const std::string& name) const {
const std::vector<Variable*>& vars = InputVars(name);
return GetDims(vars);
}
proto::VarType::Type RuntimeInferShapeContext::GetInputVarType(
const std::string& name) const {
return GetVarType(InputVars(name).at(0));
}
std::vector<proto::VarType::Type> RuntimeInferShapeContext::GetInputsVarType(
const std::string& name) const {
return GetVarTypes(InputVars(name));
}
std::vector<proto::VarType::Type> RuntimeInferShapeContext::GetOutputsVarType(
const std::string& name) const {
return GetVarTypes(OutputVars(name));
}
void RuntimeInferShapeContext::SetOutputDim(const std::string& name,
const DDim& dim) {
auto& vars = OutputVars(name);
PADDLE_ENFORCE_EQ(
vars.size(),
1UL,
common::errors::InvalidArgument("Output(%s) should hold one element, "
"but now it holds %zu elements.",
name,
vars.size()));
SetDim(vars[0], dim);
}
void RuntimeInferShapeContext::SetOutputsDim(const std::string& name,
const std::vector<DDim>& dims) {
auto& vars = OutputVars(name);
SetDims(vars, dims);
}
const phi::ArgumentMappingFn*
RuntimeInferShapeContext::GetPhiArgumentMappingFn() const {
return phi::OpUtilsMap::Instance().GetArgumentMappingFn(op_.Type());
}
const phi::KernelSignature*
RuntimeInferShapeContext::GetPhiDefaultKernelSignature() const {
return &phi::DefaultKernelSignatureMap::Instance().Get(op_.Type());
}
void RuntimeInferShapeContext::SetSkipLoD(bool skip) { can_skip_lod_ = skip; }
bool RuntimeInferShapeContext::HasRuntimeAttributes() const {
bool is_runtime = false;
if (phi::DefaultKernelSignatureMap::Instance().Has(op_.Type())) {
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
GetPhiDefaultKernelSignature()->name);
if (!phi_kernels.empty()) {
const auto& args_def = phi_kernels.cbegin()->second.args_def();
const auto& attr_defs = args_def.attribute_defs();
for (size_t i = 0; i < attr_defs.size(); ++i) {
if (attr_defs[i].type_index == phi::AttributeType::SCALAR ||
attr_defs[i].type_index == phi::AttributeType::INT_ARRAY) {
is_runtime = true;
break;
}
}
}
} else {
is_runtime = true;
}
return is_runtime;
}
std::vector<LegacyLoD> RuntimeInferShapeContext::GetOutputsLod(
const std::string& out) const {
auto out_it = ctx_.outputs.find(out);
auto& out_var_list = out_it->second;
std::vector<LegacyLoD> ret;
for (auto* out_var : out_var_list) {
if (out_var != nullptr) {
auto* out_tensor = out_var->GetMutable<DenseTensor>();
ret.push_back(out_tensor->lod());
}
}
return ret;
}
std::vector<DDim> RuntimeInferShapeContext::GetOutputsDim(
const std::string& name) const {
const std::vector<Variable*>& vars = OutputVars(name);
std::vector<Variable*> vars_res;
for (auto var : vars) {
if (var != nullptr) {
vars_res.push_back(var);
}
}
return GetDims(vars_res);
}
DDim RuntimeInferShapeContext::GetDim(Variable* var) const {
PADDLE_ENFORCE_NOT_NULL(
var, common::errors::InvalidArgument("Input variable is nullptr."));
if (var->IsType<DenseTensor>()) {
return var->Get<DenseTensor>().dims();
} else if (var->IsType<phi::SelectedRows>()) {
return var->Get<phi::SelectedRows>().GetCompleteDims();
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"Only DenseTensor or SelectedRows support 'GetDim', but input "
"Variable's type is %s.",
ToTypeName(var->Type())));
}
}
std::vector<DDim> RuntimeInferShapeContext::GetDims(
const std::vector<Variable*>& vars) const {
std::vector<DDim> ret;
ret.reserve(vars.size());
std::transform(
vars.begin(), vars.end(), std::back_inserter(ret), [this](Variable* var) {
return this->GetDim(var);
});
return ret;
}
std::vector<DDim> RuntimeInferShapeContext::GetRepeatedDims(
const std::string& name) const {
PADDLE_THROW(common::errors::PreconditionNotMet(
"GetRepeatedDims method only ban be used in compile time."));
}
void RuntimeInferShapeContext::SetDim(Variable* var, const DDim& dim) {
if (var->IsType<DenseTensor>()) {
var->GetMutable<DenseTensor>()->Resize(dim);
} else if (var->IsType<phi::SelectedRows>()) {
var->GetMutable<phi::SelectedRows>()->set_height(dim[0]);
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Variable type error, expect DenseTensor or SelectedRows, but "
"received "
"(%s).",
ToTypeName(var->Type())));
}
}
void RuntimeInferShapeContext::SetDims(const std::vector<Variable*>& vars,
const std::vector<DDim>& dims) {
size_t length = vars.size();
PADDLE_ENFORCE_EQ(length,
dims.size(),
common::errors::InvalidArgument(
"The number of input variables do not match the "
"number of input dimensions, the number of variables "
"is %zu, the number of dimensions is %zu.",
length,
dims.size()));
for (size_t i = 0; i < length; ++i) {
if (vars[i] == nullptr) {
continue;
}
SetDim(vars[i], dims[i]);
}
}
void RuntimeInferShapeContext::SetRepeatedDims(const std::string& name,
const std::vector<DDim>& dims) {
PADDLE_THROW(common::errors::PreconditionNotMet(
"SetRepeatedDims method only can be used in compile time."));
}
std::vector<proto::VarType::Type> RuntimeInferShapeContext::GetVarTypes(
const std::vector<Variable*>& vars) const {
std::vector<proto::VarType::Type> retv;
retv.resize(vars.size());
std::transform(
vars.begin(),
vars.end(),
retv.begin(),
std::bind(std::mem_fn(&RuntimeInferShapeContext::GetVarType), // NOLINT
this,
std::placeholders::_1));
return retv;
}
proto::VarType::Type RuntimeInferShapeContext::GetVarType(Variable* var) const {
return ToVarType(var->Type());
}
const std::vector<Variable*>& RuntimeInferShapeContext::InputVars(
const std::string& name) const {
auto it = ctx_.inputs.find(name);
PADDLE_ENFORCE_NE(
it,
ctx_.inputs.end(),
common::errors::NotFound(
"Operator (%s) does not have the input (%s).", op_.Type(), name));
return it->second;
}
const std::vector<Variable*>& RuntimeInferShapeContext::OutputVars(
const std::string& name) const {
auto it = ctx_.outputs.find(name);
PADDLE_ENFORCE_NE(
it,
ctx_.outputs.end(),
common::errors::NotFound(
"Operator (%s) does not have the outputs (%s).", op_.Type(), name));
return it->second;
}
void OperatorBase::Run(const Scope& scope, const Place& place) {
try {
VLOG(4) << place << " " << DebugStringEx(&scope);
if (phi::is_gpu_place(place)) {
#if !defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP)
PADDLE_THROW(common::errors::Unavailable(
"Cannot run operator on place %s, please recompile paddle or "
"reinstall Paddle with CUDA support.",
place));
#else
auto dev_id = place.device;
platform::SetDeviceId(dev_id);
#endif
} else if (phi::is_xpu_place(place)) {
#ifndef PADDLE_WITH_XPU
PADDLE_THROW(common::errors::Unavailable(
"Cannot run operator on place %s, please recompile paddle or "
"reinstall Paddle with XPU support.",
place));
#else
auto dev_id = place.device;
platform::SetXPUDeviceId(dev_id);
#endif
} else if (phi::is_custom_place(place)) {
#ifndef PADDLE_WITH_CUSTOM_DEVICE
PADDLE_THROW(common::errors::Unavailable(
"Cannot run operator on place %s, please recompile paddle or "
"reinstall Paddle with CustomDevice support.",
place));
#else
phi::DeviceManager::SetDevice(place);
#endif
}
{
// TODO(wangchaochaohu) : refine code to use only one RecordEvent)
// in order to record different op type cost time
// and different op name cost time,we set two event.
phi::RecordEvent op_type_record_event(
Type(), phi::TracerEventType::Operator, 1);
auto op_name = platform::OpName(outputs_, Type());
phi::RecordEvent op_name_record_event(
op_name,
phi::TracerEventType::Operator,
FLAGS_enable_host_event_recorder_hook ? 20 : 1,
phi::EventRole::kUniqueOp);
RunImpl(scope, place);
}
VLOG(3) << GetExecutionPlace(place) << " " << DebugStringEx(&scope);
} catch (platform::EnforceNotMet& exception) {
framework::InsertCallStackInfo(Type(), Attrs(), &exception);
throw exception;
} catch (platform::EOFException&) {
std::rethrow_exception(std::current_exception());
} catch (std::exception& ex) {
LOG(WARNING) << Type() << " raises an exception "
<< common::demangle(typeid(ex).name()) << ", " << ex.what();
std::rethrow_exception(std::current_exception());
} catch (...) {
LOG(WARNING) << Type() << " raises an unknown exception";
std::rethrow_exception(std::current_exception());
}
}
bool OperatorBase::HasInputs(const std::string& name) const {
return inputs_.find(name) != inputs_.end();
}
std::string OperatorBase::Input(const std::string& name) const {
auto& ins = Inputs(name);
PADDLE_ENFORCE_LE(
ins.size(),
1UL,
common::errors::InvalidArgument(
"Operator %s's input %s should contain only one variable.",
type_,
name));
return ins.empty() ? kEmptyVarName : ins[0];
}
const std::vector<std::string>& OperatorBase::Inputs(
const std::string& name) const {
auto it = inputs_.find(name);
PADDLE_ENFORCE_NE(
it,
inputs_.end(),
common::errors::NotFound(
"Operator %s does not have the input %s.", type_, name));
return it->second;
}
bool OperatorBase::HasOutputs(const std::string& name) const {
if (outputs_.find(name) != outputs_.end()) {
return true;
} else {
return false;
}
}
std::string OperatorBase::Output(const std::string& name) const {
auto& outs = Outputs(name);
PADDLE_ENFORCE_LE(
outs.size(),
1UL,
common::errors::InvalidArgument(
"Operator %s's output %s should contain only one variable.",
type_,
name));
return outs.empty() ? kEmptyVarName : outs[0];
}
const std::vector<std::string>& OperatorBase::Outputs(
const std::string& name) const {
auto it = outputs_.find(name);
PADDLE_ENFORCE_NE(
it,
outputs_.end(),
common::errors::NotFound(
"Operator %s does not have an output called %s.", type_, name));
return it->second;
}
std::string OperatorBase::DebugStringEx(const Scope* scope) const {
std::stringstream ss;
ss << "Op(" << type_ << "), inputs:{";
const std::unordered_set<std::string>* no_need_buffer_vars = nullptr;
if (info_ && info_->NoNeedBufferVarsInferer()) {
no_need_buffer_vars =
&(Info().NoNeedBufferVarsInferer()(Inputs(), Outputs(), Attrs()));
if (no_need_buffer_vars->empty()) no_need_buffer_vars = nullptr;
}
for (auto it = inputs_.begin(); it != inputs_.end();) {
auto& input = *it;
bool is_no_need_buffer_var =
(no_need_buffer_vars && no_need_buffer_vars->count(input.first) > 0);
ss << input.first << "[";
for (size_t i = 0; i < input.second.size(); ++i) {
auto var_name = input.second[i];
ss << var_name;
if (scope) {
if (!VarInited(*scope, var_name)) {
ss << "[uninited]";
} else {
int row_size = GetRowSize(*scope, var_name);
if (row_size >= 0) {
ss << "[row_size=" << row_size << "]";
}
std::string dtype = is_no_need_buffer_var
? "unknown_dtype"
: GetDtype(*scope, var_name);
std::string place = is_no_need_buffer_var
? "unknown_place"
: GetPlace(*scope, var_name);
ss << ":" << dtype;
ss << "[" << GetDimsDebug(*scope, var_name, true) << "]";
ss << "(" << GetLoDDebug(*scope, var_name) << ")";
ss << "(" << place << ")";
}
}
if (i != input.second.size() - 1) {
ss << ", ";
}
}
ss << "]";
++it;
if (it != inputs_.end()) {
ss << ", ";
}
}
ss << "}, outputs:{";
for (auto it = outputs_.begin(); it != outputs_.end();) {
auto& output = *it;
ss << output.first << "[";
for (size_t i = 0; i < output.second.size(); ++i) {
auto var_name = output.second[i];
ss << var_name;
if (scope) {
if (!VarInited(*scope, var_name)) {
ss << "[uninited]";
} else {
int row_size = GetRowSize(*scope, output.second[i]);
if (row_size >= 0) {
ss << "[row_size=" << row_size << "]";
}
std::string dtype = GetDtype(*scope, output.second[i]);
ss << ":" << dtype;
ss << "[" << GetDimsDebug(*scope, var_name, true) << "]";
ss << "(" << GetLoDDebug(*scope, var_name) << ")";
ss << "(" << GetPlace(*scope, var_name) << ")";
}
}
if (i != output.second.size() - 1) {
ss << ", ";
}
}
ss << "]";
++it;
if (it != outputs_.end()) {
ss << ", ";
}
}
ss << "}.";
return ss.str();
}
OperatorBase::OperatorBase(const std::string& type,
const VariableNameMap& inputs,
const VariableNameMap& outputs,
const AttributeMap& attrs)
: type_(type),
inputs_(inputs),
outputs_(outputs),
attrs_(attrs),
// NOTE(zjl): why op_info may be nullptr?
info_(OpInfoMap::Instance().GetNullable(type)),
output_hookfuncs_(),
input_hookfuncs_() {
// In dygraph mode, all the OperatorBase will be constructed by function:
// framework::OpRegistry::CreateOp(type, {}, {}, {}, false).
// Inputs, outputs and attrs will be set to empty map
// to improve the execution efficiency of dygraph.
if (!inputs_.empty() || !outputs_.empty()) {
GenerateTemporaryNames();
CheckAllInputOutputSet();
}
// canonicalize attrs
if (info_ && info_->proto_) {
CanonicalizeScalarAttrs(*info_->proto_, &attrs_);
}
// In OperatorBase level, all attributes with VarDesc type will be considered
// as Input.
for (auto& attr : FilterAttrVar(attrs)) {
VLOG(3) << "found Attribute with Variable type: " << attr.first;
inputs_[attr.first] = AttrVarNames(attr.second);
attrs_.erase(attr.first);
}
}
std::vector<std::string> OperatorBase::InputVars() const {
std::vector<std::string> ret_val;
for (auto& o : inputs_) {
ret_val.reserve(ret_val.size() + o.second.size());
ret_val.insert(ret_val.end(), o.second.begin(), o.second.end());
}
return ret_val;
}
std::vector<std::string> OperatorBase::OutputVars(bool has_intermediate) const {
std::vector<std::string> ret_val;
if (has_intermediate) {
// push all outputs into ret_val
for (auto& o : outputs_) {
ret_val.reserve(ret_val.size() + o.second.size());
ret_val.insert(ret_val.end(), o.second.begin(), o.second.end());
}
return ret_val;
}
auto& info = Info();
// get all OpProto::Var for outputs
for (auto& o : info.Proto().outputs()) {
// ignore all intermediate output
if (o.intermediate()) continue;
auto out = outputs_.find(o.name());
if (out != outputs_.end()) {
ret_val.reserve(ret_val.size() + out->second.size());
ret_val.insert(ret_val.end(), out->second.begin(), out->second.end());
}
}
return ret_val;
}
void OperatorBase::CheckAllInputOutputSet() const {
if (info_ == nullptr || info_->proto_ == nullptr) return;
for (auto& in : info_->Proto().inputs()) {
if (!in.dispensable() && !in.extra()) {
PADDLE_ENFORCE_NE(
inputs_.find(in.name()),
inputs_.end(),
common::errors::NotFound(
"Operator %s's input (%s) is not set.", Type(), in.name()));
}
}
for (auto& out : info_->Proto().outputs()) {
if (!out.dispensable() && !out.extra() && !out.intermediate()) {
PADDLE_ENFORCE_NE(
outputs_.find(out.name()),
outputs_.end(),
common::errors::NotFound(
"Operator %s's output (%s) is not set.", Type(), out.name()));
}
}
}
void OperatorBase::GenerateTemporaryNames() {
static std::atomic<size_t> gUniqId(0UL);
for (auto& output : outputs_) {
for (auto& output_name : output.second) {
if (output_name == kTempVarName) {
output_name += type_;
output_name += "@";
output_name += std::to_string(gUniqId.fetch_add(1));
}
}
}
}
const DenseTensor* GetDenseTensorOrSelectedRowsValueFromVar(
const Variable& var) {
if (var.IsType<DenseTensor>()) {
return static_cast<const DenseTensor*>(&(var.Get<DenseTensor>()));
} else if (var.IsType<phi::SelectedRows>()) {
return &(var.Get<phi::SelectedRows>().value());
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"Variable type is %s, expect DenseTensor or SelectedRows.",
ToTypeName(var.Type())));
}
}
DenseTensor* GetMutableDenseTensorOrSelectedRowsValueFromVar(Variable* var) {
if (var->IsType<DenseTensor>()) {
return var->GetMutable<DenseTensor>();
} else if (var->IsType<phi::SelectedRows>()) {
return var->GetMutable<phi::SelectedRows>()->mutable_value();
} else {
PADDLE_THROW(common::errors::InvalidArgument(
"Variable type is %s, expect DenseTensor or SelectedRows.",
ToTypeName(var->Type())));
}
}
OperatorWithKernel::OperatorWithKernel(const std::string& type,
const VariableNameMap& inputs,
const VariableNameMap& outputs,
const AttributeMap& attrs)
: OperatorBase(type, inputs, outputs, attrs), impl_(nullptr) {}
OperatorWithKernel::~OperatorWithKernel() = default;
bool ExecutionContext::HasInput(const std::string& name) const {
auto* var = InputVar(name);
return var != nullptr;
}
bool ExecutionContext::HasInputs(const std::string& name) const {
const auto& ins = ctx_.inputs;
auto it = ins.find(name);
if (it == ins.end() || it->second.empty()) {
return false;
}
for (const auto* input : it->second) {
if (input == nullptr) {
return false;
}
}
return true;
}
bool ExecutionContext::HasOutput(const std::string& name) const {
auto* var = OutputVar(name);
return var != nullptr;
}
const Variable* ExecutionContext::InputVar(const std::string& name) const {
auto it = ctx_.inputs.find(name);
if (it == ctx_.inputs.end()) return nullptr;
PADDLE_ENFORCE_LE(
it->second.size(),
1UL,
common::errors::InvalidArgument(
"Operator %s's input %s should contain only one variable.",
op_.Type(),
name));
return it->second.empty() ? nullptr : it->second[0];
}
Variable* ExecutionContext::OutputVar(const std::string& name) const {
auto it = ctx_.outputs.find(name);
if (it == ctx_.outputs.end()) return nullptr;
PADDLE_ENFORCE_LE(
it->second.size(),
1UL,
common::errors::InvalidArgument(
"Operator %s's output %s should contain only one variable.",
op_.Type(),
name));
return it->second.empty() ? nullptr : it->second[0];
}
template <>
const std::vector<const DenseTensor*> ExecutionContext::MultiInput<DenseTensor>(
const std::string& name) const {
auto vars = MultiInputVar(name);
if (vars.empty()) {
return {};
}
std::vector<const DenseTensor*> res;
res.reserve(vars.size());
std::transform(vars.begin(),
vars.end(),
std::back_inserter(res),
[&](const Variable* var) -> const DenseTensor* {
if (var == nullptr) return nullptr;
PADDLE_ENFORCE_EQ(
var->IsType<DenseTensor>(),
true,
common::errors::InvalidArgument(
"Input variable should be DenseTensor, "
"but the received type is %s.",
ToTypeName(var->Type())));
return &(var->Get<DenseTensor>());
});
return res;
}
template <>
std::vector<DenseTensor*> ExecutionContext::MultiOutput<DenseTensor>(
const std::string& name) const {
auto vars = MultiOutputVar(name);
if (vars.empty()) {
return {};
}
std::vector<DenseTensor*> res;
res.reserve(vars.size());
std::transform(vars.begin(),
vars.end(),
std::back_inserter(res),
[&](Variable* var) -> DenseTensor* {
return var == nullptr ? nullptr
: var->GetMutable<DenseTensor>();
});
return res;
}
bool OpSupportGPU(const std::string& op_type) {
// check in new Function kernel first
bool has_phi_kernel = false;
auto& kernel_factory = phi::KernelFactory::Instance();
auto kernel_key_map =
kernel_factory.SelectKernelMap(phi::TransToPhiKernelName(op_type));
for (auto& kernel : kernel_key_map) {
has_phi_kernel = true;
if (phi::is_gpu_place(phi::TransToPhiPlace(kernel.first.backend()))) {
return true;
}
}
auto& all_kernels = OperatorWithKernel::AllOpKernels();
auto it = all_kernels.find(op_type);
if (it != all_kernels.end()) {
for (auto& kern_pair : it->second) {
if (phi::is_gpu_place(kern_pair.first.place_)) {
return true;
}
}
} else {
if (has_phi_kernel) {
// if has phi kernel, but not find phi gpu kernel and fluid gpu kernel,
// this op doesn't support GPU
return false;
} else {
// All control operator must support GPU
return true;
}
}
return false;
}
struct OperatorWithKernel::CacheImpl {
static const char kNotAllowInferShapeCache[]; // NOLINT
explicit CacheImpl(phi::KernelContext* kernel_ctx,
RuntimeInferShapeContext* infer_shape_ctx,
const std::vector<DenseTensor*>& tensors,
bool not_allow_infer_shape_cache)
: kernel_ctx_(kernel_ctx),
infer_shape_ctx_(infer_shape_ctx),
tensors_(tensors),
not_allow_infer_shape_cache_(not_allow_infer_shape_cache),
last_ddims_() {}
phi::KernelContext* getKernelContext() { return kernel_ctx_.get(); }
RuntimeInferShapeContext* getRuntimeInferShapeContext() {
return infer_shape_ctx_.get();
}
bool NeedInferShape() {
if (not_allow_infer_shape_cache_) return true;
bool ret{false};
if (last_ddims_.empty() || tensors_.empty()) ret = true;
if (!ret) {
PADDLE_ENFORCE_EQ(
last_ddims_.size(),
tensors_.size(),
common::errors::InvalidArgument(
"The size of last_ddims_ should be equal to tensors_. "));
for (size_t i = 0; i < last_ddims_.size(); ++i) {
if (tensors_[i]->dims() != last_ddims_[i]) {
ret = true;
break;
}
}
}
if (ret) {
last_ddims_.resize(tensors_.size());
for (size_t i = 0; i < last_ddims_.size(); ++i) {
last_ddims_[i] = tensors_[i]->dims();
}
}
VLOG(3) << "need infer shape is " << ret;
return ret;
}
private:
std::unique_ptr<phi::KernelContext> kernel_ctx_;
std::unique_ptr<RuntimeInferShapeContext> infer_shape_ctx_;
std::vector<DenseTensor*> tensors_;
bool not_allow_infer_shape_cache_;
std::vector<DDim> last_ddims_;
};
const char // NOLINT
OperatorWithKernel::CacheImpl::kNotAllowInferShapeCache[] =
"@NOT_ALLOW_INFERSHAPE_CACHE@";
static void CheckTensorNANOrInf(const std::string& op_type,
const std::string& name,
const DenseTensor& tensor) {
if (tensor.memory_size() == 0) {
return;
}
if (framework::TransToProtoVarType(tensor.dtype()) != proto::VarType::FP32 &&
framework::TransToProtoVarType(tensor.dtype()) != proto::VarType::FP64) {
return;
}
PADDLE_ENFORCE_NE(
framework::TensorContainsInf(tensor),
true,
common::errors::Fatal(
"Operator %s output DenseTensor %s contains Inf.", op_type, name));
PADDLE_ENFORCE_NE(
framework::TensorContainsNAN(tensor),
true,
common::errors::Fatal(
"Operator %s output DenseTensor %s contains NAN.", op_type, name));
}
bool OperatorWithKernel::SupportGPU() const {
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
phi::TransToPhiKernelName(type_));
auto has_phi_kernel =
std::any_of(phi_kernels.begin(),
phi_kernels.end(),
[](phi::KernelKeyMap::const_reference kern_pair) {
return kern_pair.first.backend() == phi::Backend::GPU;
});
if (has_phi_kernel) {
return true;
} else {
auto kernel_iter = OperatorWithKernel::AllOpKernels().find(type_);
if (kernel_iter == OperatorWithKernel::AllOpKernels().end()) {
return false;
} else {
auto& op_kernels = kernel_iter->second;
return std::any_of(op_kernels.begin(),
op_kernels.end(),
[](OpKernelMap::const_reference kern_pair) {
return phi::is_gpu_place(kern_pair.first.place_);
});
}
}
}
bool OperatorWithKernel::SupportXPU() const {
#ifdef PADDLE_WITH_XPU
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
phi::TransToPhiKernelName(type_));
auto has_phi_kernel =
std::any_of(phi_kernels.begin(),
phi_kernels.end(),
[](phi::KernelKeyMap::const_reference kern_pair) {
return kern_pair.first.backend() == phi::Backend::XPU;
});
if (has_phi_kernel) {
return true;
} else {
auto kernel_iter = OperatorWithKernel::AllOpKernels().find(type_);
if (kernel_iter == OperatorWithKernel::AllOpKernels().end()) {
return false;
} else {
auto& op_kernels = kernel_iter->second;
return std::any_of(
op_kernels.begin(),
op_kernels.end(),
[this](OpKernelMap::const_reference kern_pair) {
bool is_xpu_support1 = phi::backends::xpu::is_xpu_support_op(
type_, phi::TransToPhiDataType(kern_pair.first.data_type_));
bool is_xpu_support2 = phi::backends::xpu::is_xpu_support_op(
phi::TransToPhiKernelName(type_),
phi::TransToPhiDataType(kern_pair.first.data_type_));
return phi::is_xpu_place(kern_pair.first.place_) &&
(is_xpu_support1 || is_xpu_support2);
});
}
}
#else
PADDLE_THROW(common::errors::PreconditionNotMet(
"should not call OperatorWithKernel::SupportXPU() when not compiled with "
"XPU support."));
return false;
#endif
}
bool OperatorWithKernel::SupportCustomDevice() const {
#ifdef PADDLE_WITH_CUSTOM_DEVICE
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
phi::TransToPhiKernelName(type_));
auto has_phi_kernel =
std::any_of(phi_kernels.begin(),
phi_kernels.end(),
[](phi::KernelKeyMap::const_reference kern_pair) {
return phi::is_custom_place(
phi::TransToPhiPlace(kern_pair.first.backend()));
});
if (has_phi_kernel) {
return true;
} else {
auto kernel_iter = OperatorWithKernel::AllOpKernels().find(type_);
if (kernel_iter == OperatorWithKernel::AllOpKernels().end()) {
return false;
} else {
auto& op_kernels = kernel_iter->second;
return std::any_of(op_kernels.begin(),
op_kernels.end(),
[this](OpKernelMap::const_reference kern_pair) {
return phi::is_custom_place(kern_pair.first.place_);
});
}
}
#else
PADDLE_THROW(common::errors::PreconditionNotMet(
"should not call OperatorWithKernel::SupportCustomDevice() when not "
"compiled with "
"CustomDevice support."));
return false;
#endif
}
bool OperatorWithKernel::SupportsONEDNN(const DataType data_type) const {
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
phi::TransToPhiKernelName(type_));
auto has_phi_kernel =
std::any_of(phi_kernels.begin(),
phi_kernels.end(),
[data_type](phi::KernelKeyMap::const_reference kern_pair) {
return kern_pair.first.backend() == phi::Backend::ONEDNN &&
kern_pair.first.dtype() == data_type;
});
if (has_phi_kernel) {
return true;
} else {
auto op_kernel_iter = OperatorWithKernel::AllOpKernels().find(type_);
if (op_kernel_iter == OperatorWithKernel::AllOpKernels().end()) {
return false;
} else {
auto& op_kernels = op_kernel_iter->second;
return std::any_of(
op_kernels.begin(),
op_kernels.end(),
[data_type](OpKernelMap::const_reference kern_pair) {
return phi::is_cpu_place(kern_pair.first.place_) &&
kern_pair.first.library_type_ == LibraryType::kMKLDNN &&
kern_pair.first.data_type_ ==
paddle::framework::TransToProtoVarType(data_type);
});
}
}
}
bool OperatorWithKernel::SupportsCUDNN(const DataType data_type) const {
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
phi::TransToPhiKernelName(type_));
auto has_phi_kernel =
std::any_of(phi_kernels.begin(),
phi_kernels.end(),
[data_type](phi::KernelKeyMap::const_reference kern_pair) {
return kern_pair.first.backend() == phi::Backend::GPUDNN &&
kern_pair.first.dtype() == data_type;
});
if (has_phi_kernel) {
return true;
} else {
auto op_kernel_iter = OperatorWithKernel::AllOpKernels().find(type_);
if (op_kernel_iter == OperatorWithKernel::AllOpKernels().end()) {
return false;
} else {
auto& op_kernels = op_kernel_iter->second;
proto::VarType::Type fluid_data_type =
framework::TransToProtoVarType(data_type);
return std::any_of(
op_kernels.begin(),
op_kernels.end(),
[fluid_data_type](OpKernelMap::const_reference kern_pair) {
return phi::is_gpu_place(kern_pair.first.place_) &&
kern_pair.first.library_type_ == LibraryType::kCUDNN &&
kern_pair.first.data_type_ == fluid_data_type;
});
}
}
}
bool OperatorWithKernel::SupportsCPUBF16() const {
auto phi_kernels = phi::KernelFactory::Instance().SelectKernelMap(
phi::TransToPhiKernelName(type_));
auto has_phi_kernel =
std::any_of(phi_kernels.begin(),
phi_kernels.end(),
[](phi::KernelKeyMap::const_reference kern_pair) {
return kern_pair.first.backend() == phi::Backend::CPU &&
kern_pair.first.dtype() == DataType::BFLOAT16;
});
if (has_phi_kernel) {
return true;
} else {
auto op_kernel_iter = OperatorWithKernel::AllOpKernels().find(type_);
if (op_kernel_iter == OperatorWithKernel::AllOpKernels().end()) {
return false;
} else {
auto& op_kernels = op_kernel_iter->second;
return std::any_of(op_kernels.begin(),
op_kernels.end(),
[](OpKernelMap::const_reference kern_pair) {
return phi::is_cpu_place(kern_pair.first.place_) &&
kern_pair.first.place_ == CPUPlace() &&
kern_pair.first.data_type_ ==
proto::VarType::Type::VarType_Type_BF16;
});
}
}
}
bool OperatorWithKernel::SupportsKernelType(
const OpKernelType& kernel_type, const ExecutionContext& exe_ctx) const {
auto& all_op_kernels = AllOpKernels();
auto kernels_iter = all_op_kernels.find(type_);
if (kernels_iter == all_op_kernels.end()) return false;
OpKernelMap& kernels = kernels_iter->second;
auto kernel_iter = kernels.find(kernel_type);
#if defined(PADDLE_WITH_XPU) && !defined(PADDLE_WITH_XPU_KP)
if (phi::is_xpu_place(kernel_type.place_)) {
return kernel_iter != kernels.end() &&
paddle::platform::is_xpu_support_op(
type_, phi::TransToPhiDataType(kernel_type.data_type_));
}
#endif
#ifdef PADDLE_WITH_XPU_KP
if (phi::is_xpu_place(kernel_type.place_)) {
bool use_xpu_kp_kernel_rt =
FLAGS_run_kp_kernel &&
paddle::platform::is_xpu_kp_support_op(
type_, phi::TransToPhiDataType(kernel_type.data_type_));
bool use_xpu_kp_kernel_debug =
paddle::platform::is_in_xpu_kpwhite_list(type_);
bool is_xpu_kp_support = (use_xpu_kp_kernel_rt || use_xpu_kp_kernel_debug);
if (is_xpu_kp_support) {
auto tmp_kernel_type = kernel_type;
tmp_kernel_type.library_type_ = LibraryType::kKP;
return kernels.find(tmp_kernel_type) != kernels.end();
}
return kernel_iter != kernels.end() &&
paddle::platform::is_xpu_support_op(
type_, phi::TransToPhiDataType(kernel_type.data_type_));
}
#endif
// NOTE(jiahongyu): If OneDNN can be used, the function SupportsKernelType needs
// to check whether current op supports OneDNN kernel. There are three
// statements in if condition:
// 1. Whether onednn kernel fallbacks to plain kernel;
// 2. Whether this op has specific implementation;
// 3. Whether onednn kernel can be used.
#ifdef PADDLE_WITH_DNNL
if (!this->DnnFallback() && !paddle::platform::in_onednn_white_list(type_) &&
this->CanONEDNNBeUsed(exe_ctx, kernel_type.data_type_)) {
auto tmp_kernel_type = kernel_type;
tmp_kernel_type.library_type_ = framework::LibraryType::kMKLDNN;
tmp_kernel_type.data_layout_ = framework::DataLayout::ONEDNN;
return kernels.find(tmp_kernel_type) != kernels.end();
}
#endif
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
defined(PADDLE_WITH_CUSTOM_DEVICE)
if (this->CanCUDNNBeUsed(exe_ctx, kernel_type.data_type_)) {
auto tmp_kernel_type = kernel_type;
tmp_kernel_type.library_type_ = framework::LibraryType::kCUDNN;
return kernels.find(tmp_kernel_type) != kernels.end();
}
#endif
return kernel_iter != kernels.end();
}
bool OperatorWithKernel::CanONEDNNBeUsed(const framework::ExecutionContext& ctx,
DataType data_type) const {
return ((ctx.HasAttr("use_mkldnn") && ctx.Attr<bool>("use_mkldnn")) ||
(ctx.HasAttr("use_onednn") && ctx.Attr<bool>("use_onednn"))) &&
phi::is_cpu_place(ctx.GetPlace()) && this->SupportsONEDNN(data_type);
}
bool OperatorWithKernel::CanONEDNNBeUsed(const framework::ExecutionContext& ctx,
proto::VarType::Type data_type) const {
return this->CanONEDNNBeUsed(ctx, phi::TransToPhiDataType(data_type));
}
bool OperatorWithKernel::CanCUDNNBeUsed(const framework::ExecutionContext& ctx,
DataType data_type) const {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
defined(PADDLE_WITH_CUSTOM_DEVICE)
bool use_cudnn = ctx.HasAttr("use_cudnn") && ctx.Attr<bool>("use_cudnn") &&
(phi::is_gpu_place(ctx.GetPlace()) ||
phi::is_custom_place(ctx.GetPlace()));
if (use_cudnn) {
const auto& dev_ctx = ctx.device_context<phi::DeviceContext>();
use_cudnn &= (dev_ctx.cudnn_handle() != nullptr);
}
#if defined(PADDLE_WITH_CUDA)
if (use_cudnn && data_type == DataType::BFLOAT16) {
PADDLE_ENFORCE_GE(
platform::DnnVersion(),
8100,
common::errors::InvalidArgument(
"bfloat16 can only be used when CUDNN_VERSION >= 8100"));
}
#endif // PADDLE_WITH_CUDA
return use_cudnn && this->SupportsCUDNN(data_type);
#endif
return false;
}
bool OperatorWithKernel::CanCUDNNBeUsed(const framework::ExecutionContext& ctx,
proto::VarType::Type data_type) const {
return this->CanCUDNNBeUsed(ctx, phi::TransToPhiDataType(data_type));
}
void OperatorWithKernel::InferShape(InferShapeContext* ctx) const {
PADDLE_THROW(common::errors::PermissionDenied(
"The default InferShape function of OperatorWithKernel is not allowed to "
"be called, please override corresponding InferShape function in the "
"specific operator."));
}
void OperatorWithKernel::RuntimeInferShape(const Scope& scope,
const Place& place,
const RuntimeContext& ctx) const {
RuntimeInferShapeContext infer_shape_ctx(*this, ctx);
this->Info().infer_shape_(&infer_shape_ctx);
}
template <typename T>
bool HasSameTensorType(phi::TensorBase* phi_tensor, Variable* var) {
if (phi_tensor == nullptr && var == nullptr) {
return true;
} else if (phi_tensor != nullptr && var != nullptr) {
if (T::classof(phi_tensor) && var->IsType<T>()) {
return true;
}
}
return false;
}
// TODO(YuanRisheng): We need collect all `need_prepare_phi_data_`
// into this function.
void OperatorWithKernel::CheckWhetherPreparePhiData(
const VariableNameMap& innames,
const VariableNameMap& outnames,
const Scope& scope) const {
if (run_phi_kernel_ && impl_ != nullptr) {
const auto& phi_kernel_context = impl_->getKernelContext();
size_t phi_tensor_index = 0;
// Check each tensor in KernelContext, if there is a tensor that has
// different type with variable. The PhiKernelContext need be reconstructed.
// We use kernel_signature_'s output to retrieve tensor. Because the tensor
// in phi_kernel_context stored in the order of kernel_signature_'s output.
if (phi_kernel_context->OutputsSize() >= phi_tensor_index ||
kernel_signature_ == nullptr) {
need_prepare_phi_data_ = true;
return;
}
const auto& phi_output_names = kernel_signature_->output_names;
for (auto& phi_output_name : phi_output_names) {
const auto& iter = outnames.find(phi_output_name);
if (iter != outnames.end()) {
for (auto& var_name : iter->second) {
auto var_output = scope.FindVar(var_name);
auto phi_output =
phi_kernel_context->MutableOutputAt<phi::TensorBase>(
phi_tensor_index);
if (phi_output == nullptr) {
continue;
}
if (!(HasSameTensorType<DenseTensor>(phi_output, var_output) ||
HasSameTensorType<phi::SparseCooTensor>(phi_output,
var_output) ||
HasSameTensorType<phi::Strings>(phi_output, var_output))) {
need_prepare_phi_data_ = true;
}
phi_tensor_index++;
}
}
}
}
}
void OperatorWithKernel::RunImpl(const Scope& scope, const Place& place) const {
// To reduce the elapsed time of HasAttr, we use bool variable to record the
// result of HasAttr.
if (!enable_cache_runtime_context_ && HasAttr(kEnableCacheRuntimeContext))
enable_cache_runtime_context_ = true;
if (!all_kernels_must_compute_runtime_shape_ &&
HasAttr(kAllKernelsMustComputeRuntimeShape))
all_kernels_must_compute_runtime_shape_ = true;
const Scope* cur_scope = &scope;
CheckWhetherPreparePhiData(Inputs(), Outputs(), scope);
#if defined(PADDLE_WITH_XPU)
if (std::getenv("XPU_NEED_PREPARE_PHI_DATA") != nullptr) {
need_prepare_phi_data_ = atoi(std::getenv("XPU_NEED_PREPARE_PHI_DATA"));
}
#endif
if (!enable_cache_runtime_context_) {
RuntimeContext ctx(Inputs(), Outputs(), scope);
RunImpl(scope, place, &ctx);
} else if (run_phi_kernel_ && impl_ != nullptr && !need_prepare_data_ &&
!need_prepare_phi_data_) {
if (!all_kernels_must_compute_runtime_shape_ && impl_->NeedInferShape()) {
this->Info().infer_shape_(impl_->getRuntimeInferShapeContext());
}
(*phi_kernel_)(impl_->getKernelContext());
} else {
if (runtime_ctx_.get() == nullptr || pre_scope_ != cur_scope) {
std::lock_guard<std::mutex> lock(cache_update_mutex_);
if (runtime_ctx_.get() == nullptr || pre_scope_ != cur_scope) {
runtime_ctx_ =
std::make_unique<RuntimeContext>(Inputs(), Outputs(), scope);
pre_scope_ = cur_scope;
}
}
RunImpl(scope, place, runtime_ctx_.get());
}
}
void OperatorWithKernel::RunImpl(const Scope& scope,
const Place& place,
RuntimeContext* runtime_ctx) const {
phi::DeviceContextPool& pool = phi::DeviceContextPool::Instance();
bool fallback_to_cpu = false;
phi::KernelKey phi_cpu_kernel_key;
auto* dev_ctx = pool.Get(place);
// using cache
if (kernel_type_.get()) {
dev_ctx = pool.Get(kernel_type_->place_);
}
auto exe_ctx = ExecutionContext(*this, scope, *dev_ctx, *runtime_ctx);
// TODO(Liu-xiandong): Now we are using too much if-else and hard code in XPU
// device, it's ugly, and we will refactor in the future.
#if defined(PADDLE_WITH_XPU_KP)
bool use_phi_xpu_kp = false;
#endif
// TODO(chenweihang): Now we are still reusing a lot of the original fluid
// implementation, this is a gradual replacement process
// TODO(chenweihang): in the first phase of project, we only support CPU, CUDA
// and RCOM backend, the XPU, NPU and OneDNN will be supported in the second
// phase
phi::KernelKey phi_kernel_key;
std::string phi_kernel_name;
if (phi::KernelFactory::Instance().HasCompatiblePhiKernel(type_)) {
if (kernel_signature_ == nullptr || phi_kernel_ == nullptr) {
if (phi::KernelFactory::Instance().HasStructuredKernel(
type_)) { // NOLINT
kernel_signature_ =
std::make_unique<phi::KernelSignature>(type_.c_str());
} else {
kernel_signature_ = std::make_unique<phi::KernelSignature>(
GetExpectedPhiKernelArgs(exe_ctx));
}
VLOG(6) << *kernel_signature_.get();
phi_kernel_name = kernel_signature_->name;
kernel_type_ =
std::make_unique<OpKernelType>(InnerGetExpectedKernelType(exe_ctx));
dev_ctx = pool.Get(kernel_type_->place_);
// NOTE(Liu-xiandong): The register kernel used KP have library_type[KP],
// But the default library_type is Plain, so we need to modify the
// library_type here, otherwise it can't work.
#ifdef PADDLE_WITH_XPU_KP
if (phi::is_xpu_place(kernel_type_->place_)) {
bool use_xpu_kp_kernel_rt =
FLAGS_run_kp_kernel &&
paddle::platform::is_xpu_kp_support_op(
type_, phi::TransToPhiDataType(kernel_type_->data_type_));
bool use_xpu_kp_kernel_debug =
paddle::platform::is_in_xpu_kpwhite_list(type_);
if (use_xpu_kp_kernel_rt) {
VLOG(3) << "phi xpu_kp using rt mode in static graph";
}
if (use_xpu_kp_kernel_debug) {
VLOG(3) << "phi xpu_kp using debug mode in static graph";
}
bool is_xpu_kp_support =
(use_xpu_kp_kernel_rt || use_xpu_kp_kernel_debug);
if (is_xpu_kp_support) {
auto expected_kernel_key_library_type = kernel_type_->library_type_;
kernel_type_->library_type_ = LibraryType::kKP;
VLOG(3) << "modifying XPU KP kernel in static graph: "
<< phi_kernel_name
<< ", using_kernel_key:" << *kernel_type_.get();
auto try_phi_kernel_key =
TransOpKernelTypeToPhiKernelKey(*kernel_type_.get());
if (!phi::KernelFactory::Instance().HasKernel(phi_kernel_name,
try_phi_kernel_key)) {
kernel_type_->library_type_ = expected_kernel_key_library_type;
VLOG(3) << "modify XPU KP kernel in static graph: "
<< phi_kernel_name << " is failed " << *kernel_type_.get();
} else {
use_phi_xpu_kp = true;
VLOG(3) << "modify XPU KP kernel in static graph: "
<< phi_kernel_name << " is succeed " << *kernel_type_.get();
}
}
}
#endif
phi_kernel_key = TransOpKernelTypeToPhiKernelKey(*kernel_type_.get());
phi_kernel_ = std::make_unique<phi::Kernel>(
phi::KernelFactory::Instance().SelectKernel(phi_kernel_name,
phi_kernel_key));
if (phi_kernel_->IsValid()) {
VLOG(6) << "Static graph mode ChoosePhiKernel - kernel name: "
<< phi_kernel_name << " | kernel key: " << phi_kernel_key
<< " | kernel: " << *phi_kernel_;
} else {
VLOG(1) << "Static graph mode ChoosePhiKernel - kernel `"
<< phi_kernel_name << "` not found.";
}
} else {
phi_kernel_name = kernel_signature_->name;
// NOTE(jiahongyu): The registered OneDNN kernel have library_type =
// LibraryType::kMKLDNN and data_layout_ = DataLayout::ONEDNN. But the default
// values are kPlain, so we need to modify the library_type and data_layout_
// here. There are three statements in if condition:
// 1. Whether onednn kernel fallbacks to plain kernel;
// 2. Whether this op has specific implementation;
// 3. Whether onednn kernel can be used.
#ifdef PADDLE_WITH_DNNL
if (!this->DnnFallback() &&
!paddle::platform::in_onednn_white_list(type_) &&
this->CanONEDNNBeUsed(exe_ctx, kernel_type_->data_type_)) {
kernel_type_->library_type_ = framework::LibraryType::kMKLDNN;
kernel_type_->data_layout_ = framework::DataLayout::ONEDNN;
} else if (phi::is_cpu_place(kernel_type_->place_) &&
kernel_type_->data_type_ ==
proto::VarType::Type::VarType_Type_BF16 &&
!this->SupportsCPUBF16() &&
this->SupportsONEDNN(DataType::BFLOAT16)) {
kernel_type_->library_type_ = framework::LibraryType::kMKLDNN;
kernel_type_->data_layout_ = framework::DataLayout::ONEDNN;
}
#endif
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (this->CanCUDNNBeUsed(exe_ctx, kernel_type_->data_type_)) {
kernel_type_->library_type_ = framework::LibraryType::kCUDNN;
}
#endif
// NOTE(Liu-xiandong):In my ctest, this branch do not be executed,
// I can't understand it, it's really confusing.
// But we still need to keep this to avoid errors.
#ifdef PADDLE_WITH_XPU_KP
if (phi::is_xpu_place(kernel_type_->place_)) {
bool use_xpu_kp_kernel_rt =
FLAGS_run_kp_kernel &&
paddle::platform::is_xpu_kp_support_op(
type_, phi::TransToPhiDataType(kernel_type_->data_type_));
bool use_xpu_kp_kernel_debug =
paddle::platform::is_in_xpu_kpwhite_list(type_);
if (use_xpu_kp_kernel_rt) {
VLOG(3) << "phi xpu_kp using rt mode in static graph";
}
if (use_xpu_kp_kernel_debug) {
VLOG(3) << "phi xpu_kp using debug mode in static graph";
}
bool is_xpu_kp_support =
(use_xpu_kp_kernel_rt || use_xpu_kp_kernel_debug);
if (is_xpu_kp_support) {
auto expected_kernel_key_library_type = kernel_type_->library_type_;
kernel_type_->library_type_ = LibraryType::kKP;
VLOG(3) << "modifying XPU KP kernel in static graph: "
<< phi_kernel_name
<< ", using_kernel_key:" << *kernel_type_.get();
auto try_phi_kernel_key =
TransOpKernelTypeToPhiKernelKey(*kernel_type_.get());
if (!phi::KernelFactory::Instance().HasKernel(phi_kernel_name,
try_phi_kernel_key)) {
kernel_type_->library_type_ = expected_kernel_key_library_type;
VLOG(3) << "modify XPU KP kernel in static graph: "
<< phi_kernel_name << " is failed " << *kernel_type_.get();
} else {
use_phi_xpu_kp = true;
VLOG(3) << "modify XPU KP kernel in static graph: "
<< phi_kernel_name << " is succeed " << *kernel_type_.get();
}
}
}
#endif
phi_kernel_key = TransOpKernelTypeToPhiKernelKey(*kernel_type_.get());
}
// NOTE(Liu-xiandong): Determine whether the selected kernel is valid
// If not, use the kernel registered in fluid. And if the fluid do not
// contains the related heterogeneous kernel, use phi CPU kernel.
#if defined(PADDLE_WITH_XPU)
bool is_xpu_unsupported =
phi::is_xpu_place(kernel_type_->place_) &&
!paddle::platform::is_xpu_support_op(
type_, phi::TransToPhiDataType(kernel_type_->data_type_));
#endif
#ifdef PADDLE_WITH_XPU_KP
bool use_xpu_kp_kernel_rt =
phi::is_xpu_place(kernel_type_->place_) && FLAGS_run_kp_kernel &&
paddle::platform::is_xpu_kp_support_op(
type_, phi::TransToPhiDataType(kernel_type_->data_type_));
bool use_xpu_kp_kernel_debug =
phi::is_xpu_place(kernel_type_->place_) &&
paddle::platform::is_in_xpu_kpwhite_list(type_);
bool is_xpu_kp_support = (use_xpu_kp_kernel_rt || use_xpu_kp_kernel_debug);
#endif
bool in_custom_back_list = false;
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
in_custom_back_list =
phi::backends::custom_device::is_in_custom_black_list(phi_kernel_name);
#endif
if (phi_kernel_->IsValid() && !in_custom_back_list
#if defined(PADDLE_WITH_XPU) && !defined(PADDLE_WITH_XPU_KP)
&& !is_xpu_unsupported
#endif
#if defined(PADDLE_WITH_XPU_KP)
&& (!is_xpu_unsupported || use_phi_xpu_kp)
#endif
) {
run_phi_kernel_ = true;
} else {
auto& all_op_kernels = AllOpKernels();
auto kernels_iter = all_op_kernels.find(type_);
// NOTE(Liu-xiandong): If we can't find heterogeneous kernel in phi,
// we need to select the heterogeneous kernel in fluid, but the kernel
// registered in KP use library_type[KP], we need to modify it.
#ifdef PADDLE_WITH_XPU_KP
if (is_xpu_kp_support) {
kernel_type_->library_type_ = LibraryType::kKP;
}
#endif
if (kernels_iter == all_op_kernels.end() ||
kernels_iter->second.find(*kernel_type_.get()) ==
kernels_iter->second.end()
#if defined(PADDLE_WITH_XPU) && !defined(PADDLE_WITH_XPU_KP)
|| is_xpu_unsupported
#endif
#if defined(PADDLE_WITH_XPU_KP)
|| (is_xpu_unsupported && !is_xpu_kp_support)
#endif
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
|| in_custom_back_list
#endif
) {
fallback_to_cpu = true;
if (in_custom_back_list) {
VLOG(3) << "fluid in black list: " << phi_kernel_name;
}
phi_cpu_kernel_key = FallBackToCpu(phi_kernel_key, *this);
phi_kernel_ = std::make_unique<phi::Kernel>(
phi::KernelFactory::Instance().SelectKernel(phi_kernel_name,
phi_cpu_kernel_key));
dev_ctx = pool.Get(CPUPlace());
if (phi_kernel_->IsValid()) {
VLOG(6) << "Static graph mode PrepareImpl - kernel name: "
<< phi_kernel_name << " | kernel key: " << phi_cpu_kernel_key
<< " | kernel: " << *phi_kernel_;
run_phi_kernel_ = true;
}
}
}
}
if (!run_phi_kernel_) {
if (kernel_type_.get() == nullptr || kernel_func_.get() == nullptr) {
ChooseKernel(exe_ctx);
dev_ctx = pool.Get(kernel_type_->place_);
}
}
// do data transformScope &transfer_scope;
std::vector<std::string> transferred_inplace_vars;
Scope* transfer_scope = nullptr;
{
phi::RecordEvent record_event("prepare_data",
phi::TracerEventType::OperatorInner,
1,
phi::EventRole::kInnerOp);
if (need_prepare_data_) {
if (fallback_to_cpu) { // NOLINT
transfer_scope = PrepareData(scope,
phi_cpu_kernel_key,
&transferred_inplace_vars,
runtime_ctx,
dev_ctx->GetPlace());
} else {
transfer_scope = PrepareData(
scope,
framework::TransOpKernelTypeToPhiKernelKey(*kernel_type_),
&transferred_inplace_vars,
runtime_ctx,
dev_ctx->GetPlace());
}
}
}
// exec scope is the scope that kernel actually executed on.
const Scope& exec_scope =
(transfer_scope == nullptr ? scope : *transfer_scope);
if (!all_kernels_must_compute_runtime_shape_) {
phi::RecordEvent record_event("infer_shape",
phi::TracerEventType::OperatorInner,
1,
phi::EventRole::kInnerOp);
RuntimeInferShapeContext infer_shape_ctx(*this, *runtime_ctx);
this->Info().infer_shape_(&infer_shape_ctx);
record_event.End();
platform::RecordOpInfoSupplement(
Type(), Attrs(), infer_shape_ctx, *runtime_ctx, Id());
}
// TODO(panyx0718): ExecutionContext should only depend on RuntimeContext
// not Scope. Imperative mode only pass inputs and get outputs.
{
phi::RecordEvent record_event("compute",
phi::TracerEventType::OperatorInner,
1,
phi::EventRole::kInnerOp);
if (run_phi_kernel_ && phi_kernel_->GetKernelRegisteredType() ==
phi::KernelRegisteredType::FUNCTION) {
phi::KernelContext phi_kernel_context;
if (enable_cache_runtime_context_ && !need_prepare_phi_data_ &&
!need_prepare_data_) {
// TODO(inference): Now we only support dense_tensor cache, we may be
// support ScalarTensor, SparseTensor in future.
bool all_dense_tensor_input_{true};
for (auto& iter : Inputs()) {
for (auto& name : iter.second) {
all_dense_tensor_input_ &=
scope.FindVar(name)->IsType<DenseTensor>();
}
}
std::vector<DenseTensor*> tensors;
if (all_dense_tensor_input_) {
for (auto& iter : Inputs()) {
for (auto& name : iter.second) {
auto* t = scope.FindVar(name)->GetMutable<DenseTensor>();
tensors.push_back(t);
}
}
}
impl_ = std::make_unique<CacheImpl>(
new phi::KernelContext(),
new RuntimeInferShapeContext(*this, *runtime_ctx),
tensors,
HasAttr(CacheImpl::kNotAllowInferShapeCache));
BuildPhiKernelContext(*runtime_ctx, dev_ctx, impl_->getKernelContext());
(*phi_kernel_)(impl_->getKernelContext());
} else {
phi::KernelContext phi_kernel_context;
// Do data transform before building KernelContext
// TODO(zhiqiu): support TransferInplaceVarsBack
BuildPhiKernelContext(*runtime_ctx, dev_ctx, &phi_kernel_context);
(*phi_kernel_)(&phi_kernel_context);
}
} else if (run_phi_kernel_ && phi_kernel_->GetKernelRegisteredType() ==
phi::KernelRegisteredType::STRUCTURE) {
ExecutionContext execution_context(
*this, exec_scope, *dev_ctx, *runtime_ctx);
(*phi_kernel_)(&execution_context);
} else {
(*kernel_func_)(
ExecutionContext(*this, exec_scope, *dev_ctx, *runtime_ctx));
}
if (fallback_to_cpu) {
phi_kernel_.reset();
}
}
if (!transferred_inplace_vars.empty()) {
// there is inplace variable has been transferred.
TransferInplaceVarsBack(scope, transferred_inplace_vars, *transfer_scope);
}
// See [ Why need handle complex gradient to real gradient? ]
// Only handle the case where the current kernel data type is complex
if (framework::IsComplexType(kernel_type_->data_type_)) {
HandleComplexGradToRealGrad(scope, runtime_ctx);
}
/*For profiling/benchmark only*/
if (FLAGS_benchmark) {
dev_ctx->Wait();
#if defined(PADDLE_WITH_CUDA) || defined(PADLDE_WITH_ROCM)
PADDLE_ENFORCE_GPU_SUCCESS(platform::GpuGetLastError());
#endif
VLOG(4) << "Operator(" << Type() << "): context wait and get last error";
}
if (FLAGS_check_nan_inf) {
try {
framework::details::CheckOpHasNanOrInf(*this, exec_scope, place);
} catch (...) {
const std::vector<std::string>* callstack = nullptr;
auto attrs = Attrs();
auto iter =
attrs.find(OpProtoAndCheckerMaker::OpCreationCallstackAttrName());
if (iter != attrs.end()) {
callstack = &PADDLE_GET_CONST(std::vector<std::string>, iter->second);
if (callstack->empty()) callstack = nullptr;
}
std::ostringstream sout;
if (callstack) {
if (FLAGS_call_stack_level > 1) {
sout << "\n\n Compile Traceback (most recent call last):";
} else {
sout << "In user code:\n";
}
for (auto& line : *callstack) {
sout << "\n " << line;
}
}
std::cout << sout.str() << std::endl;
std::rethrow_exception(std::current_exception());
}
}
// To solve issue #15032, have a discussion with @Luotao for cpu inference,
// do not cache transfer scope, hence in this case delete transfer scope
// after run to avoid memory leak
if (transfer_scope && !run_by_executor_ && !enable_cache_transfer_scope_) {
scope.DeleteScope(transfer_scope);
}
}
OpKernelType OperatorWithKernel::InnerGetExpectedKernelType(
const ExecutionContext& ctx) const {
phi::KernelKey phi_kernel_key = this->GetExpectedKernelType(ctx);
auto expected_kernel_key =
framework::TransPhiKernelKeyToOpKernelType(phi_kernel_key);
// NOTE(jiahongyu): PADDLE_WITH_DNNL codes are moved outside function
// GetExpectedKernelType, so that if OneDNN can be used, the library_type_ and
// data_layout_ of expected_kernel_key need to be adjusted. There are three
// statements in if condition:
// 1. Whether onednn kernel fallbacks to plain kernel;
// 2. Whether this op has specific implementation;
// 3. Whether onednn kernel can be used.
#ifdef PADDLE_WITH_DNNL
if (!this->DnnFallback() && !paddle::platform::in_onednn_white_list(type_) &&
this->CanONEDNNBeUsed(ctx, expected_kernel_key.data_type_)) {
expected_kernel_key.library_type_ = framework::LibraryType::kMKLDNN;
expected_kernel_key.data_layout_ = framework::DataLayout::ONEDNN;
} else if (phi::is_cpu_place(expected_kernel_key.place_) &&
expected_kernel_key.data_type_ ==
proto::VarType::Type::VarType_Type_BF16 &&
!this->SupportsCPUBF16() &&
this->SupportsONEDNN(DataType::BFLOAT16)) {
expected_kernel_key.library_type_ = framework::LibraryType::kMKLDNN;
expected_kernel_key.data_layout_ = framework::DataLayout::ONEDNN;
}
#endif
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
defined(PADDLE_WITH_CUSTOM_DEVICE)
if (this->CanCUDNNBeUsed(ctx, expected_kernel_key.data_type_)) {
expected_kernel_key.library_type_ = framework::LibraryType::kCUDNN;
}
#endif
if (HasAttr("op_device")) {
if (Attr<std::string>("op_device") == "cpu") {
expected_kernel_key.place_ = CPUPlace();
} else if (Attr<std::string>("op_device").find("gpu") !=
std::string::npos) {
auto device = Attr<std::string>("op_device");
size_t pos = device.find(':');
if (pos != std::string::npos) {
device = device.substr(0, pos);
LOG_FIRST_N(WARNING, 1)
<< "Device index is only supported under pipeline parallelism, "
<< "so it will be ignored.";
}
// when the Op that does not have GPUKernel is assigned to GPU, the
// CPUKernel will be executed and a warning will be given at the same
// time.
expected_kernel_key.place_ = CPUPlace();
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (SupportGPU()) {
auto& dev_ctx = ctx.device_context();
expected_kernel_key.place_ = dev_ctx.GetPlace();
}
#endif
if (phi::is_cpu_place(expected_kernel_key.place_)) {
LOG_FIRST_N(WARNING, 1)
<< "Op(" << type_
<< ") has no CUDA implementation. It will be assigned to CPUPlace.";
}
} else if (Attr<std::string>("op_device").find("npu") !=
std::string::npos) {
auto device = Attr<std::string>("op_device");
size_t pos = device.find(':');
if (pos != std::string::npos) {
device = device.substr(0, pos);
LOG_FIRST_N(WARNING, 1)
<< "Device index is only supported under pipeline parallelism, "
<< "so it will be ignored.";
}
// when the Op that does not have NPUKernel is assigned to NPU, the
// CPUKernel will be executed and a warning will be given at the same
// time.
expected_kernel_key.place_ = CPUPlace();
#ifdef PADDLE_WITH_CUSTOM_DEVICE
if (SupportCustomDevice()) {
auto& dev_ctx = ctx.device_context();
expected_kernel_key.place_ = dev_ctx.GetPlace();
}
#endif
if (phi::is_cpu_place(expected_kernel_key.place_)) {
LOG_FIRST_N(WARNING, 1)
<< "Op(" << type_
<< ") has no NPU implementation. It will be assigned to CPUPlace.";
}
} else if (Attr<std::string>("op_device").find("xpu") !=
std::string::npos) {
auto device = Attr<std::string>("op_device");
size_t pos = device.find(':');
if (pos != std::string::npos) {
device = device.substr(0, pos);
LOG_FIRST_N(WARNING, 1)
<< "Device index is only supported under pipeline parallelism, "
<< "so it will be ignored.";
}
// when the Op that does not have XPUKernel is assigned to XPU, the
// CPUKernel will be executed and a warning will be given at the same
// time.
expected_kernel_key.place_ = CPUPlace();
#ifdef PADDLE_WITH_XPU
if (SupportXPU()) {
auto& dev_ctx = ctx.device_context();
expected_kernel_key.place_ = dev_ctx.GetPlace();
}
#endif
if (phi::is_cpu_place(expected_kernel_key.place_)) {
LOG_FIRST_N(WARNING, 1)
<< "Op(" << type_
<< ") has no XPU implementation. It will be assigned to CPUPlace.";
}
}
}
if (phi::places_are_same_class(expected_kernel_key.place_, ctx.GetPlace())) {
expected_kernel_key.place_ = ctx.GetPlace();
}
VLOG(3) << "op type:" << type_
<< ", expected_kernel_key:" << expected_kernel_key;
return expected_kernel_key;
}
phi::KernelKey OperatorWithKernel::ChoosePhiKernel(
const ExecutionContext& ctx) const {
std::string phi_kernel_name;
if (phi::KernelFactory::Instance().HasStructuredKernel(type_)) { // NOLINT
kernel_signature_ = std::make_unique<phi::KernelSignature>(type_.c_str());
} else {
kernel_signature_ =
std::make_unique<phi::KernelSignature>(GetExpectedPhiKernelArgs(ctx));
}
VLOG(6) << *kernel_signature_.get();
phi_kernel_name = kernel_signature_->name;
kernel_type_ =
std::make_unique<OpKernelType>(InnerGetExpectedKernelType(ctx));
auto phi_kernel_key = TransOpKernelTypeToPhiKernelKey(*kernel_type_.get());
phi_kernel_ =
std::make_unique<phi::Kernel>(phi::KernelFactory::Instance().SelectKernel(
phi_kernel_name, phi_kernel_key));
if (phi_kernel_->IsValid()) {
VLOG(6) << "Static graph mode ChoosePhiKernel - kernel name: "
<< phi_kernel_name << " | kernel key: " << phi_kernel_key
<< " | kernel: " << *phi_kernel_;
} else {
VLOG(1) << "Static graph mode ChoosePhiKernel - kernel `" << phi_kernel_name
<< "` not found.";
}
return phi_kernel_key;
}
void OperatorWithKernel::ChooseKernel(const ExecutionContext& ctx) const {
// check if op[type] has kernel registered.
auto& all_op_kernels = AllOpKernels();
auto kernels_iter = all_op_kernels.find(type_);
PADDLE_ENFORCE_NE(
kernels_iter,
all_op_kernels.end(),
common::errors::Unimplemented(
"There are no kernels which are registered in the %s operator.",
type_));
OpKernelMap& kernels = kernels_iter->second;
auto expected_kernel_key = InnerGetExpectedKernelType(ctx);
auto kernel_iter = kernels.find(expected_kernel_key);
#ifdef PADDLE_WITH_DNNL
// workaround for missing ONEDNN kernel when FLAGS_use_mkldnn or
// FLAGS_use_onednn env var is set
if (kernel_iter == kernels.end() &&
expected_kernel_key.library_type_ == LibraryType::kMKLDNN) {
VLOG(3) << "missing ONEDNN kernel: fallbacking to PLAIN one";
expected_kernel_key.library_type_ = LibraryType::kPlain;
expected_kernel_key.data_layout_ = DataLayout::kAnyLayout;
kernel_iter = kernels.find(expected_kernel_key);
}
#endif
#if defined(PADDLE_WITH_XPU) && !defined(PADDLE_WITH_XPU_KP)
if (phi::is_xpu_place(expected_kernel_key.place_) &&
(kernel_iter == kernels.end() ||
!paddle::platform::is_xpu_support_op(
type_, phi::TransToPhiDataType(expected_kernel_key.data_type_)))) {
VLOG(3) << "fluid missing XPU kernel: " << type_
<< ", expected_kernel_key:" << expected_kernel_key
<< ", fallbacking to CPU one!";
expected_kernel_key.place_ = CPUPlace();
kernel_iter = kernels.find(expected_kernel_key);
}
#endif
#ifdef PADDLE_WITH_XPU_KP
if (phi::is_xpu_place(expected_kernel_key.place_)) {
bool use_xpu_kp_kernel_rt =
FLAGS_run_kp_kernel &&
paddle::platform::is_xpu_kp_support_op(
type_, phi::TransToPhiDataType(expected_kernel_key.data_type_));
bool use_xpu_kp_kernel_debug =
paddle::platform::is_in_xpu_kpwhite_list(type_);
if (use_xpu_kp_kernel_rt) {
VLOG(3) << "fluid xpu_kp using rt mode ";
}
if (use_xpu_kp_kernel_debug) {
VLOG(3) << "fluid xpu_kp using debug mode ";
}
bool is_xpu_kp_support = (use_xpu_kp_kernel_rt || use_xpu_kp_kernel_debug);
if (is_xpu_kp_support) {
auto cache_expected_kernel_key_library_type =
expected_kernel_key.library_type_;
expected_kernel_key.library_type_ = LibraryType::kKP;
kernel_iter = kernels.find(expected_kernel_key);
// if can't find corresponding kernel when is_xpu_kp_support is on
// if the fluid do not register related kernel, it can't work and have
// error as before
if (kernel_iter == kernels.end()) {
expected_kernel_key.library_type_ =
cache_expected_kernel_key_library_type;
expected_kernel_key.place_ = CPUPlace();
kernel_iter = kernels.find(expected_kernel_key);
} else {
VLOG(3) << "fluid using XPU KP kernel: " << type_
<< ", using_kernel_key:" << expected_kernel_key;
}
}
bool is_xpu_unsupported = (!paddle::platform::is_xpu_support_op(
type_, phi::TransToPhiDataType(expected_kernel_key.data_type_)));
if (!is_xpu_kp_support &&
(kernel_iter == kernels.end() || is_xpu_unsupported)) {
VLOG(3) << "fluid missing XPU kernel: " << type_
<< ", expected_kernel_key:" << expected_kernel_key
<< ", fallbacking to CPU one!";
expected_kernel_key.place_ = CPUPlace();
kernel_iter = kernels.find(expected_kernel_key);
}
}
#endif
#ifdef PADDLE_WITH_IPU
if (kernel_iter == kernels.end() &&
phi::is_ipu_place(expected_kernel_key.place_)) {
VLOG(3) << "missing IPU kernel: " << type_
<< ", expected_kernel_key:" << expected_kernel_key
<< ", fallbacking to CPU one!";
expected_kernel_key.place_ = CPUPlace();
kernel_iter = kernels.find(expected_kernel_key);
}
#endif
#ifdef PADDLE_WITH_CUSTOM_DEVICE
if (kernel_iter == kernels.end() &&
phi::is_custom_place(expected_kernel_key.place_)) {
VLOG(3) << "missing " << expected_kernel_key.place_.GetDeviceType()
<< " kernel: " << type_
<< ", expected_kernel_key:" << expected_kernel_key
<< ", fallbacking to CPU one!";
expected_kernel_key.place_ = CPUPlace();
kernel_iter = kernels.find(expected_kernel_key);
}
#endif
PADDLE_ENFORCE_NE(
kernel_iter,
kernels.end(),
common::errors::NotFound("Operator (%s) does not have kernel for %s.",
type_,
KernelTypeToString(expected_kernel_key)));
std::lock_guard<std::mutex> lock(cache_update_mutex_);
if (kernel_type_.get() == nullptr || kernel_func_.get() == nullptr) {
kernel_type_ = std::make_unique<OpKernelType>(expected_kernel_key);
kernel_func_ = std::make_unique<OpKernelFunc>(kernel_iter->second);
}
}
void OperatorWithKernel::TransferInplaceVarsBack(
const Scope& scope,
const std::vector<std::string>& inplace_vars,
const Scope& transfer_scope) const {
for (auto& var_name : inplace_vars) {
VLOG(3) << "share inplace var " + var_name + " back to it's original scope";
auto* origin_var = scope.FindVar(var_name);
PADDLE_ENFORCE_NOT_NULL(origin_var,
common::errors::InvalidArgument(
"The variable[%s] is nullptr.", var_name));
auto* original_tensor =
GetMutableDenseTensorOrSelectedRowsValueFromVar(origin_var);
auto* var = transfer_scope.FindVar(var_name);
PADDLE_ENFORCE_NOT_NULL(var,
common::errors::InvalidArgument(
"The variable[%s] is nullptr.", var_name));
auto* transformed_tensor = GetDenseTensorOrSelectedRowsValueFromVar(*var);
original_tensor->ShareDataWith(*transformed_tensor);
}
}
void OperatorWithKernel::HandleComplexGradToRealGrad(
const Scope& scope, RuntimeContext* ctx) const {
for (auto& var_name_item : Outputs()) {
std::vector<Variable*>& output_vars = ctx->outputs[var_name_item.first];
for (size_t i = 0; i < var_name_item.second.size(); ++i) {
// 1. find grad_var & check whether is complex tensor
auto var_name = var_name_item.second[i];
auto orig_var_name = GradOriginalVarName(var_name);
// only focus on gradient var
if (var_name == orig_var_name) {
continue;
}
auto* grad_var = output_vars[i];
// skip nullptr var
if (grad_var == nullptr) {
continue;
}
// don't process phi::TensorArray temporarily,
// add support if necessary for complex number calculations in the future
if (!VarIsTensor(*grad_var)) {
continue;
}
auto* grad_tensor =
GetMutableDenseTensorOrSelectedRowsValueFromVar(grad_var);
// skip nullptr tensor
if (grad_tensor == nullptr || !grad_tensor->IsInitialized()) {
continue;
}
// only focus on complex dtype now
auto src_type = framework::TransToProtoVarType(grad_tensor->dtype());
if (!IsComplexType(src_type)) {
continue;
}
// 2. find forward var & check whether need to cast
auto* var = scope.FindVar(orig_var_name);
// if forward var not exists, do nothing
if (var == nullptr) {
continue;
}
if (!VarIsTensor(*var)) {
continue;
}
const auto* tensor = GetDenseTensorOrSelectedRowsValueFromVar(*var);
PADDLE_ENFORCE_NOT_NULL(
tensor,
common::errors::Unavailable(
"Forward tensor is nullptr when handle complex data to real."));
// only need record type, the allocation may have been released
auto dst_type = framework::TransToProtoVarType(tensor->dtype());
// only focus on real dtype and need casting
if (IsComplexType(dst_type)) {
continue;
}
// 3. cast complex grad to real grad
VLOG(6) << "Transform " << framework::DataTypeToString(src_type)
<< " var `" << var_name << "` to "
<< framework::DataTypeToString(dst_type)
<< " real var in static graph.";
DenseTensor out;
TransComplexToReal(dst_type, src_type, *grad_tensor, &out);
SetTensorToVariable(*grad_var, out, grad_var);
}
}
}
Scope* OperatorWithKernel::PrepareData(
const Scope& scope,
const phi::KernelKey& expected_kernel_key,
std::vector<std::string>* transferred_inplace_vars,
RuntimeContext* ctx,
const Place& place) const {
Scope* new_scope = nullptr;
const std::unordered_set<std::string>* no_buffer_ins = nullptr;
if (info_) {
auto& no_buffer_inferer = info_->NoNeedBufferVarsInferer();
// Some op may not register NoNeedBufferVarsInferer
if (no_buffer_inferer) {
no_buffer_ins = &(no_buffer_inferer(Inputs(), Outputs(), Attrs()));
if (no_buffer_ins->empty()) no_buffer_ins = nullptr;
}
}
auto has_infer_varkernel_fn =
(run_phi_kernel_ && phi_kernel_->get_kerneltype_forvar_fn_ != nullptr);
phi::AttributeMap infer_attrs{};
auto fluid_attrs = Attrs();
phi::GetKernelTypeForVarContext infer_varkernel_context =
BuildGetKernelTypeForVarContext(expected_kernel_key,
fluid_attrs,
&infer_attrs,
has_infer_varkernel_fn);
const auto& name_map = Inputs();
auto prepare_input_data = [&](const std::string& in_name,
std::vector<Variable*>* in_vars,
const phi::TensorArgDef* in_def,
bool should_skip_input) -> void {
auto& name_vec = name_map.at(in_name);
for (size_t i = 0; i < in_vars->size(); ++i) {
const auto& var_name = name_vec[i];
auto* var = in_vars->at(i);
// Only tensor can be transfer to another device.
if (var == nullptr || !VarIsTensor(*var)) {
continue;
}
auto* tensor_in = GetDenseTensorOrSelectedRowsValueFromVar(*var);
// When no_buffer_ins then checking of DenseTensor::holder_ is
// not a thread safe. And for infershape scenario checks
// to be omitted are not really needed
if (should_skip_input == true) {
#ifdef PADDLE_WITH_DNNL
// Var without buffer may be needed
// for some situation like InferShape().
// In this situation We cannot skip Var analysis, as
// ONEDNN shape of Var may differ from NHWC Var
// In such situation corresponding resized Var
// has to be created and registered
if ((tensor_in->layout() == DataLayout::ONEDNN) &&
(var->IsType<DenseTensor>() == true) &&
(expected_kernel_key.layout() != DataLayout::ONEDNN) &&
(phi::OneDNNContext::tls().get_cur_paddle_data_layout() ==
DataLayout::NHWC) &&
(tensor_in->dims().size() >= 3)) {
// Mixed execution : oneDNN and GPU is not supported!
if (!new_scope) {
new_scope = &scope.NewScope();
}
auto* trans_var = new_scope->Var(var_name);
in_vars->at(i) = trans_var;
auto out = trans_var->GetMutable<DenseTensor>();
out->Resize(tensor_in->dims());
phi::funcs::MatchShapeToLayout(
out, tensor_in->layout(), DataLayout::NHWC);
VLOG(7) << "Created reshaped dummy input based on ONEDNN "
"DenseTensor , "
"but NHWC layout"
<< in_name << " in Operator " << type_;
} else {
VLOG(7) << "Skip scanning input " << in_name << " in Operator "
<< type_;
}
#endif
continue;
}
if (!tensor_in->IsInitialized()) {
continue;
}
auto kernel_type_for_var =
GetKernelTypeForVar(in_name, *tensor_in, expected_kernel_key);
if (has_infer_varkernel_fn) {
infer_varkernel_context.SetVarName(const_cast<std::string*>(&in_name));
infer_varkernel_context.SetDenseTensor(
const_cast<DenseTensor*>(tensor_in));
kernel_type_for_var =
phi_kernel_->get_kerneltype_forvar_fn_(&infer_varkernel_context);
}
bool need_trans_dtype =
NeedTransformDataType(expected_kernel_key, kernel_type_for_var);
bool need_trans_layout = NeedTransformLayout(
kernel_type_for_var.layout(), expected_kernel_key.layout());
if (!need_trans_dtype && !need_trans_layout) {
if (!run_phi_kernel_ &&
backends_are_same_class(kernel_type_for_var.backend(),
expected_kernel_key.backend())) {
continue;
}
}
std::unique_ptr<phi::KernelKey> new_expected_kernel_key = nullptr;
if (run_phi_kernel_) {
if (phi_kernel_->GetKernelRegisteredType() ==
phi::KernelRegisteredType::STRUCTURE) {
if (!backends_are_same_class(kernel_type_for_var.backend(),
expected_kernel_key.backend())) {
new_expected_kernel_key =
std::make_unique<phi::KernelKey>(expected_kernel_key.backend(),
expected_kernel_key.layout(),
expected_kernel_key.dtype());
}
} else if (in_def != nullptr && // KernelRegisteredType is Function
in_def->backend != phi::Backend::ALL_BACKEND &&
kernel_type_for_var.backend() != phi::Backend::ALL_BACKEND) {
auto tensor_backend = phi::TransToPhiBackend(tensor_in->place());
if ((in_def->backend != tensor_backend &&
!(in_def->backend == phi::Backend::GPUDNN &&
tensor_backend ==
paddle::experimental::get_accelerat_backend()) &&
!(in_def->backend == phi::Backend::KPS &&
tensor_backend == phi::Backend::XPU) &&
!(in_def->backend == phi::Backend::ONEDNN &&
tensor_backend == phi::Backend::CPU)) ||
tensor_in->place().GetType() == AllocationType::GPUPINNED) {
new_expected_kernel_key =
std::make_unique<phi::KernelKey>(in_def->backend,
expected_kernel_key.layout(),
expected_kernel_key.dtype());
}
}
}
if (!need_trans_dtype && !need_trans_layout) {
if (run_phi_kernel_ && new_expected_kernel_key == nullptr) {
continue;
}
}
VLOG(3) << "Transform Variable " << var_name << " from "
<< kernel_type_for_var << " to "
<< (new_expected_kernel_key ? *new_expected_kernel_key
: expected_kernel_key);
// In the inference scenario, the scopes will be reused across the
// batches, so the `new_scope` here will result in GPU memory explosion
// over the running of operators.
// We use a thread_local cache to fix that issue, the key in the cache is
// the combination of the `scope` argument, from_kernel_type,
// target_kernel_type.
// Have a discussion with @Superjomn or the inference developers if some
// changes on this logic for this macro might not tested on the other
// scenarios.
// If this op is not called by an Executor or ParallelExecutor, it should
// called by a NaiveExecutor, the NaiveExecutor will cache the scopes and
// variables, that behavior a lot different.
//
// To solve issue #15032, have a discussion with @Luotao for cpu
// inference, for all cpu kernels cases without GPU participation, here
// not do transfer scope caching, and cpu inference performance is not
// impacted by test.
enable_cache_transfer_scope_ = false;
if (!run_by_executor_) {
if (new_expected_kernel_key) {
if (kernel_type_for_var.backend() ==
paddle::experimental::get_accelerat_backend() ||
new_expected_kernel_key->backend() ==
paddle::experimental::get_accelerat_backend() ||
kernel_type_for_var.backend() == phi::Backend::GPUDNN ||
new_expected_kernel_key->backend() == phi::Backend::GPUDNN) {
new_scope = TryCreateTransferScope(
kernel_type_for_var, *new_expected_kernel_key, &scope);
enable_cache_transfer_scope_ = true;
}
} else if (kernel_type_for_var.backend() ==
paddle::experimental::get_accelerat_backend() ||
expected_kernel_key.backend() ==
paddle::experimental::get_accelerat_backend() ||
kernel_type_for_var.backend() == phi::Backend::GPUDNN ||
expected_kernel_key.backend() == phi::Backend::GPUDNN) {
new_scope = TryCreateTransferScope(
kernel_type_for_var, expected_kernel_key, &scope);
enable_cache_transfer_scope_ = true;
}
}
if (!new_scope) {
new_scope = &scope.NewScope();
}
// For inference, if a gpu model has an op which could only run on CPU,
// each result of different input will be the same with the first one.
// The reason is that if a gpu tensor is the input of a cpu kernel,
// we will create a new cpu tensor in new scope.
// However, if enable_cache_runtime_context_, we get the cpu tensor each
// time, not the gpu tensor. Thus, we set pre_scope_ = nullptr
// to trigger `new RuntimeContext()` in RunImpl().
if (enable_cache_runtime_context_) {
pre_scope_ = nullptr;
}
// Create new var with the same name in transfer scopes
auto* trans_var = new_scope->Var(var_name);
in_vars->at(i) = trans_var;
// Find if inplace exists between input and output
// If inplace exists, set the new created var to inplaced output, and
// record its name in transferred_inplace_vars.
for (auto& pair : Outputs()) {
for (size_t j = 0; j < pair.second.size(); ++j) {
if (pair.second[j] == var_name) {
VLOG(4) << "Found inplace between input(" << in_name
<< ") and output(" << pair.first
<< "), the variable name is " << var_name;
ctx->outputs[pair.first][j] = trans_var;
transferred_inplace_vars->emplace_back(var_name);
}
}
}
// Do transfer
DenseTensor out;
TransformData(
new_expected_kernel_key ? *new_expected_kernel_key
: expected_kernel_key,
kernel_type_for_var,
*tensor_in,
&out,
new_expected_kernel_key
? phi::TransToPhiPlace(new_expected_kernel_key->backend())
: place);
SetTensorToVariable(*var, out, trans_var);
}
};
if (run_phi_kernel_ && phi_kernel_->GetKernelRegisteredType() ==
phi::KernelRegisteredType::FUNCTION) {
const auto& input_names = kernel_signature_->input_names;
const auto& input_defs = phi_kernel_->args_def().input_defs();
PADDLE_ENFORCE_EQ(input_names.size(),
input_defs.size(),
common::errors::InvalidArgument(
"The size of inputs_args names (%d) must be equal to "
"the size of kernel input_defs (%d).",
input_names.size(),
input_defs.size()));
for (size_t i = 0; i < input_defs.size(); ++i) {
std::string input_name = input_names[i];
auto iter = ctx->inputs.find(input_name);
if (iter == ctx->inputs.end()) {
continue;
}
auto& ins_vector = iter->second;
bool should_skip_input =
no_buffer_ins && no_buffer_ins->count(input_name) > 0;
phi::TensorArgDef in_def = input_defs.at(i);
#ifdef PADDLE_WITH_CUSTOM_DEVICE
// When the backend of input tensor arg_def is CUSTOM, we need to set it
// to the actual backend by expected_kernel_key.
if (in_def.backend == phi::Backend::CUSTOM) {
in_def.SetBackend(expected_kernel_key.backend());
}
#endif
prepare_input_data(input_name, &ins_vector, &in_def, should_skip_input);
}
#ifdef PADDLE_WITH_DNNL
// For input that is Extra, only OneDNN will use Extra Inputs
auto& extra_input_names =
paddle::operators::ExtraInfoUtils::Instance().GetExtraInputNamesMap(
Type());
for (const auto& input_name : extra_input_names) {
auto iter = ctx->inputs.find(input_name);
if (iter == ctx->inputs.end()) {
continue;
}
bool should_skip_input =
no_buffer_ins && no_buffer_ins->count(input_name) > 0;
std::vector<Variable*>& input_vars = iter->second;
prepare_input_data(input_name, &input_vars, nullptr, should_skip_input);
}
#endif
} else {
for (auto& var_name_item : Inputs()) {
bool should_skip_input =
no_buffer_ins && no_buffer_ins->count(var_name_item.first) > 0;
std::vector<Variable*>& input_vars = ctx->inputs[var_name_item.first];
prepare_input_data(
var_name_item.first, &input_vars, nullptr, should_skip_input);
}
}
// If pre_scope = &scope, it means that scope is cached and the op is not in
// while block. If new_scope = nullptr, it means that for each input of this
// Op, there is no need to do PrepareData. So PrepareData could be skipped at
// the rest iterations to save the elapsed time.
// We do not support skipping PrepareData in while block, because the Op's
// input may be changed by subsequent Ops, which may cause an error.
// For inference, ops that behind conditional branch aren't supported well,
// so disable prepare optimization conservatively.
bool force_prepare_data = HasAttr("inference_force_prepare_data") &&
Attr<bool>("inference_force_prepare_data");
if (pre_scope_ == &scope && new_scope == nullptr && !force_prepare_data) {
need_prepare_data_ = false;
}
return new_scope;
}
void OperatorWithKernel::ParseInputDataType(
const Variable* var,
const std::string& name,
proto::VarType::Type* data_type) const {
if (var != nullptr) {
const DenseTensor* t = nullptr;
if (var->IsType<DenseTensor>()) {
t = &var->Get<DenseTensor>();
} else if (var->IsType<phi::SelectedRows>()) {
t = &(var->Get<phi::SelectedRows>().value());
} else if (var->IsType<phi::SparseCooTensor>()) {
const phi::SparseCooTensor* sp_t = &(var->Get<phi::SparseCooTensor>());
*data_type = paddle::framework::TransToProtoVarType(sp_t->dtype());
return;
} else if (var->IsType<phi::TensorArray>()) {
auto t_arr = &var->Get<phi::TensorArray>();
for (const auto& item : *t_arr) {
if (item.IsInitialized()) {
t = &(item);
}
}
}
if (t != nullptr) {
*data_type = paddle::framework::TransToProtoVarType(t->dtype());
}
}
}
void OperatorWithKernel::ParseMultiInputDataType(
const std::vector<Variable*>& vars,
const std::string& name,
proto::VarType::Type* data_type) const {
proto::VarType::Type default_data_type =
static_cast<proto::VarType::Type>(-1);
for (auto* var : vars) {
if (var != nullptr) {
const DenseTensor* t = nullptr;
if (var->IsType<DenseTensor>()) {
t = &var->Get<DenseTensor>();
} else if (var->IsType<phi::SelectedRows>()) {
t = &(var->Get<phi::SelectedRows>().value());
} else if (var->IsType<phi::SparseCooTensor>()) {
const phi::SparseCooTensor* sp_t = &(var->Get<phi::SparseCooTensor>());
PADDLE_ENFORCE_EQ(
sp_t->initialized(),
true,
common::errors::InvalidArgument("The %s Op's Input Variable `%s` "
"contains uninitialized Tensor.",
Type(),
name));
proto::VarType::Type tmp =
paddle::framework::TransToProtoVarType(sp_t->dtype());
PADDLE_ENFORCE(tmp == *data_type || *data_type == default_data_type,
common::errors::InvalidArgument(
"The DataType of %s Op's duplicable or different "
"slot Variable %s must be "
"consistent or register GetExpectedKernelType. The "
"current variable type is (%s), but the "
"previous variable type is (%s).",
Type(),
name,
DataTypeToString(tmp),
DataTypeToString(*data_type)));
*data_type = tmp;
} else if (var->IsType<phi::TensorArray>()) {
auto t_arr = &var->Get<phi::TensorArray>();
for (const auto& item : *t_arr) {
if (item.IsInitialized()) {
t = &(item);
}
}
}
if (t != nullptr) {
PADDLE_ENFORCE_EQ(t->IsInitialized(),
true,
common::errors::InvalidArgument(
"The %s Op's Input Variable `%s` "
"contains uninitialized DenseTensor.",
Type(),
name));
proto::VarType::Type tmp =
paddle::framework::TransToProtoVarType(t->dtype());
PADDLE_ENFORCE(tmp == *data_type || *data_type == default_data_type,
common::errors::InvalidArgument(
"The DataType of %s Op's duplicable or different "
"slot Variable %s must be "
"consistent or register GetExpectedKernelType. The "
"current variable type is (%s), but the "
"previous variable type is (%s).",
Type(),
name,
DataTypeToString(tmp),
DataTypeToString(*data_type)));
*data_type = tmp;
}
}
}
}
proto::VarType::Type OperatorWithKernel::IndicateDataType(
const ExecutionContext& ctx) const {
proto::VarType::Type default_data_type =
static_cast<proto::VarType::Type>(-1);
proto::VarType::Type data_type = default_data_type;
for (auto* name : ctx.InNameList()) {
if (ctx.InputSize(*name) == 1UL) {
ParseInputDataType(ctx.InputVar(*name), *name, &data_type);
} else {
ParseMultiInputDataType(ctx.MultiInputVar(*name), *name, &data_type);
}
}
PADDLE_ENFORCE_NE(
data_type,
default_data_type,
common::errors::NotFound(
"DataType should be indicated by input Variable at %s.", Type()));
return data_type;
}
proto::VarType::Type OperatorWithKernel::IndicateVarDataType(
const ExecutionContext& ctx, const std::string& name) const {
proto::VarType::Type default_data_type =
static_cast<proto::VarType::Type>(-1);
proto::VarType::Type data_type = default_data_type;
if (ctx.InputSize(name) == 1UL) {
ParseInputDataType(ctx.InputVar(name), name, &data_type);
} else {
ParseMultiInputDataType(ctx.MultiInputVar(name), name, &data_type);
}
PADDLE_ENFORCE_NE(
data_type,
default_data_type,
common::errors::InvalidArgument(
"The Input Variable(%s) of (%s) Operator used to determine kernel "
"data type is empty or not DenseTensor or SelectedRows or "
"DenseTensorArray.",
name,
Type()));
return data_type;
}
DenseTensor* OperatorWithKernel::GetTensorFormInputSafely(
const ExecutionContext& ctx, const std::string& name) const {
// 1. get variable and check
// NOTE: only supports signal input var now
// NOTE: using const_cast is because we don't have method
// can get single mutable var, and here will not change
// the var's data, only use some attribute
Variable* var = const_cast<Variable*>(ctx.InputVar(name));
PADDLE_ENFORCE_NOT_NULL(
var,
common::errors::NotFound(
"The variable %s is not found when promote complex types.", name));
// 2. get tensor and check
DenseTensor* t = nullptr;
if (var->IsType<DenseTensor>()) {
t = var->GetMutable<DenseTensor>();
} else if (var->IsType<phi::SelectedRows>()) {
t = var->GetMutable<phi::SelectedRows>()->mutable_value();
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported input variable type in complex type promotion."));
}
PADDLE_ENFORCE_NOT_NULL(t,
common::errors::InvalidArgument(
"The DenseTensor of variable %s is nullptr "
"when promote complex types.",
name));
PADDLE_ENFORCE_EQ(
t->IsInitialized(),
true,
common::errors::InvalidArgument(
"The DenseTensor in the %s Op's Input Variable %s(%s) is "
"not initialized.",
Type(),
name,
ctx.InputName(name)));
return t;
}
/** NOTE(chenweihang): For safety reasons, we now only
* perform type promotes for binary operations with
* complex type inputs, which is used to support the
* paddle quantum function.
* In other cases, the first input data type is used as
* the kernel data type.
*/
proto::VarType::Type OperatorWithKernel::IndicateOrPromoteVarDataTypes(
const ExecutionContext& ctx,
const std::string& name1,
const std::string& name2) const {
// 1. Get tensor
auto* tensor_a = GetTensorFormInputSafely(ctx, name1);
auto* tensor_b = GetTensorFormInputSafely(ctx, name2);
// 2. Get two input types
auto type_a = framework::TransToProtoVarType(tensor_a->dtype());
auto type_b = framework::TransToProtoVarType(tensor_b->dtype());
// 3. Get first input type or promote complex types
auto target_type = PromoteTypesIfComplexExists(type_a, type_b);
return target_type;
}
phi::KernelKey OperatorWithKernel::GetExpectedKernelType(
const ExecutionContext& ctx) const {
return phi::KernelKey(IndicateDataType(ctx), ctx.GetPlace());
}
phi::KernelKey OperatorWithKernel::GetKernelTypeForVar(
const std::string& var_name,
const DenseTensor& tensor,
const phi::KernelKey& expected_kernel_type) const {
#ifdef PADDLE_WITH_DNNL
// When the op is first oneDNN op (there was some non oneDNN op
// previously)
// then we also need to rotate shape NHWC -> NCWH
if ((expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
(tensor.layout() != phi::DataLayout::ONEDNN) &&
phi::OneDNNContext::tls().get_cur_paddle_data_layout() ==
phi::DataLayout::NHWC) {
return phi::KernelKey(
tensor.place(), phi::DataLayout::NHWC, expected_kernel_type.dtype());
}
#endif
return phi::KernelKey(
tensor.place(), tensor.layout(), expected_kernel_type.dtype());
}
phi::KernelSignature OperatorWithKernel::GetExpectedPhiKernelArgs(
const ExecutionContext& ctx) const {
ExecutionArgumentMappingContext arg_mapping_ctx(ctx);
if (arg_map_fn_ == nullptr) {
auto* arg_map_fn = phi::OpUtilsMap::Instance().GetArgumentMappingFn(type_);
if (arg_map_fn) {
arg_map_fn_ = std::make_unique<phi::ArgumentMappingFn>(*arg_map_fn);
} else {
auto func =
[this](
const phi::ArgumentMappingContext& ctx) -> phi::KernelSignature {
return phi::DefaultKernelSignatureMap::Instance().Get(type_);
};
arg_map_fn_ = std::make_unique<phi::ArgumentMappingFn>(func);
}
}
return (*arg_map_fn_)(arg_mapping_ctx);
}
static void SetDnnAttrIntoDeviceContext(
phi::DeviceContext* dev_ctx,
const Attribute& attr,
const std::string& attr_name,
const operators::ExtraAttrPropertySet& attr_properties) {
#ifdef PADDLE_WITH_DNNL
if (phi::OneDNNContext::classof(dev_ctx) &&
attr_properties.Support(operators::ExtraAttrProperty::ONEDNN)) {
VLOG(4) << "Runtime attr `" << attr_name << "` is passed to OneDNNContext.";
phi::OneDNNContext* one_dnn_ctx = static_cast<phi::OneDNNContext*>(dev_ctx);
switch (AttrTypeID(attr)) {
case proto::AttrType::FLOAT:
one_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(float, attr));
break;
case proto::AttrType::INT:
one_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(int, attr));
break;
case proto::AttrType::STRING:
one_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(std::string, attr));
break;
case proto::AttrType::INTS: // NOLINT
one_dnn_ctx->SetDnnAttr(attr_name,
PADDLE_GET_CONST(std::vector<int>, attr));
break;
case proto::AttrType::FLOATS:
one_dnn_ctx->SetDnnAttr(attr_name,
PADDLE_GET_CONST(std::vector<float>, attr));
break;
case proto::AttrType::BOOLEAN:
one_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(bool, attr));
break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported Attribute value type `%s` for phi.",
common::demangle(attr.type().name())));
}
}
#endif
#ifdef PADDLE_WITH_CUSTOM_DEVICE
if (phi::CustomContext::classof(dev_ctx) &&
attr_properties.Support(operators::ExtraAttrProperty::GPUDNN)) {
VLOG(4) << "Runtime attr `" << attr_name << "` is passed to CustomContext.";
phi::CustomContext* custom_dnn_ctx =
static_cast<phi::CustomContext*>(dev_ctx);
switch (AttrTypeID(attr)) {
case proto::AttrType::INT:
custom_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(int, attr));
break;
case proto::AttrType::BOOLEAN:
custom_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(bool, attr));
break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported Attribute value type `%s` for phi.",
common::demangle(attr.type().name())));
}
}
#endif
#ifdef PADDLE_WITH_CUDA
if (phi::GPUContext::classof(dev_ctx) &&
attr_properties.Support(operators::ExtraAttrProperty::GPUDNN)) {
VLOG(4) << "Runtime attr `" << attr_name << "` is passed to GPUDNNContext.";
phi::GPUContext* gpu_dnn_ctx = static_cast<phi::GPUContext*>(dev_ctx);
switch (AttrTypeID(attr)) {
case proto::AttrType::INT:
gpu_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(int, attr));
break;
case proto::AttrType::BOOLEAN:
gpu_dnn_ctx->SetDnnAttr(attr_name, PADDLE_GET_CONST(bool, attr));
break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported Attribute value type `%s` for phi.",
common::demangle(attr.type().name())));
}
}
#endif
}
void OperatorWithKernel::BuildPhiKernelContext(
const RuntimeContext& ctx,
phi::DeviceContext* dev_ctx,
phi::KernelContext* phi_kernel_context) const {
phi_kernel_context->SetDeviceContext(dev_ctx);
auto& input_names = kernel_signature_->input_names;
auto& attr_names = kernel_signature_->attr_names;
auto& output_names = kernel_signature_->output_names;
auto input_defs = phi_kernel_->args_def().input_defs();
auto attr_defs = phi_kernel_->args_def().attribute_defs();
auto output_defs = phi_kernel_->args_def().output_defs();
#if defined(PADDLE_WITH_DNNL)
if (phi::OneDNNContext::classof(dev_ctx)) {
// Onednn holds this op's variable's name and init them here.
phi::OneDNNContext* one_dnn_ctx = static_cast<phi::OneDNNContext*>(dev_ctx);
one_dnn_ctx->SetInputsName(Inputs());
one_dnn_ctx->SetOutputsName(Outputs());
}
#endif
PADDLE_ENFORCE_EQ(input_names.size(),
input_defs.size(),
common::errors::InvalidArgument(
"The size of inputs_args names (%d) must be equal to "
"the size of kernel input_defs (%d).",
input_names.size(),
input_defs.size()));
PADDLE_ENFORCE_EQ(output_names.size(),
output_defs.size(),
common::errors::InvalidArgument(
"The size of outputs_args names (%d) must be equal to "
"the size of kernel output_defs (%d).",
output_names.size(),
output_defs.size()));
PADDLE_ENFORCE_EQ(attr_names.size(),
attr_defs.size(),
common::errors::InvalidArgument(
"The size of attribute_args names (%d) must be equal "
"to the size of kernel attribute_defs (%d).",
attr_names.size(),
attr_defs.size()));
for (size_t i = 0; i < input_names.size(); ++i) {
auto it = ctx.inputs.find(input_names[i]);
// calculate the start and end index of the input tensors
size_t start_idx =
(i == 0 ? 0 : phi_kernel_context->InputRangeAt(i - 1).second);
// deal with optional here
if ((it == ctx.inputs.end() || it->second.empty()) &&
(input_defs[i].type_index ==
std::type_index(typeid(paddle::optional<DenseTensor>)) ||
input_defs[i].type_index ==
std::type_index(typeid(paddle::optional<phi::SelectedRows>)) ||
input_defs[i].type_index ==
std::type_index(
typeid(paddle::optional<std::vector<const DenseTensor*>>)))) {
phi_kernel_context->EmplaceBackInputWithoutSetRange(nullptr);
auto end_idx = start_idx + 1;
phi_kernel_context->AssignInputRange(std::make_pair(start_idx, end_idx),
i);
continue;
}
auto ins_vector = it->second;
size_t end_idx = start_idx + ins_vector.size();
for (auto* var : ins_vector) {
const phi::TensorBase* tensor_in = nullptr;
if (var->IsType<DenseTensor>()) {
tensor_in = &(var->Get<DenseTensor>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else if (var->IsType<phi::SelectedRows>()) {
tensor_in = &(var->Get<phi::SelectedRows>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else if (var->IsType<phi::SparseCooTensor>()) {
tensor_in = &(var->Get<phi::SparseCooTensor>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else if (var->IsType<phi::TensorArray>()) {
need_prepare_phi_data_ = true;
tensor_in = &(var->Get<phi::TensorArray>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else if (var->IsType<phi::Vocab>()) {
tensor_in = &(var->Get<phi::Vocab>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else if (var->IsType<phi::Strings>()) {
tensor_in = &(var->Get<phi::Strings>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else if (var->IsType<framework::FeedList>()) {
tensor_in = &(var->Get<framework::FeedList>());
phi_kernel_context->EmplaceBackInputWithoutSetRange(tensor_in);
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported input `%s` type when call pt kernel.",
framework::ToTypeName(var->Type())));
}
}
// Note: here cannot deal with vector<phi::TensorArray> input
phi_kernel_context->AssignInputRange(std::make_pair(start_idx, end_idx), i);
}
VLOG(4) << "Done inputs";
for (size_t i = 0; i < output_names.size(); ++i) {
auto it = ctx.outputs.find(output_names[i]);
size_t start_idx =
(i == 0 ? 0 : phi_kernel_context->OutputRangeAt(i - 1).second);
if (it == ctx.outputs.end() || it->second.empty()) {
VLOG(4) << "Output " << output_names[i] << " not found";
// Deal with the case that some outputs are not found or be NULL when run
// the kernel.
// For example : the outputs of matmul_grad are dx and dy,
// sometimes dx or dy may be NULL.
phi_kernel_context->EmplaceBackOutputWithoutSetRange(nullptr);
auto end_idx = start_idx + 1;
phi_kernel_context->AssignOutputRange(std::make_pair(start_idx, end_idx),
i);
continue;
}
auto& outs_vector = it->second;
size_t end_idx = start_idx + outs_vector.size();
for (auto* var : outs_vector) {
phi::TensorBase* tensor_out = nullptr;
if (var) {
if (var->template IsType<DenseTensor>()) {
tensor_out = var->template GetMutable<DenseTensor>();
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else if (var->template IsType<phi::SelectedRows>()) {
tensor_out = var->template GetMutable<phi::SelectedRows>();
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else if (var->template IsType<phi::SparseCooTensor>()) {
tensor_out = var->template GetMutable<phi::SparseCooTensor>();
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else if (var->template IsType<phi::TensorArray>()) {
tensor_out = var->template GetMutable<phi::TensorArray>();
// Note: If the input phi::TensorArray size is 0, the output
// phi::TensorArray is also 0
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else if (var->template IsType<phi::Strings>()) {
tensor_out = var->template GetMutable<phi::Strings>();
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else if (var->template IsType<phi::Vocab>()) {
tensor_out = var->template GetMutable<phi::Vocab>();
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else if (var->template IsType<phi::RawTensor>() ||
!var->IsInitialized()) {
tensor_out = var->template GetMutable<phi::RawTensor>();
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported output `%s` type when call pt kernel.",
framework::ToTypeName(var->Type())));
}
} else {
VLOG(4) << "Output " << output_names[i] << " is nullptr";
phi_kernel_context->EmplaceBackOutputWithoutSetRange(tensor_out);
}
}
phi_kernel_context->AssignOutputRange(std::make_pair(start_idx, end_idx),
i);
}
VLOG(4) << "Done outputs";
for (size_t i = 0; i < attr_names.size(); ++i) {
VLOG(6) << "BuildPhiKernelContext: " << attr_names[i] << ": "
<< attr_defs[i].type_index;
// attribute with Variable type has been placed into Inputs(), and
// we can parse them from RuntimeContext.inputs.
auto attr_iter = Attrs().find(attr_names[i]);
switch (attr_defs[i].type_index) {
case phi::AttributeType::SCALAR:
if (attr_iter != Attrs().end()) {
// scalar is in the attribute
switch (AttrTypeID(attr_iter->second)) {
case proto::AttrType::FLOAT:
phi_kernel_context->EmplaceBackAttr(
phi::Scalar(PADDLE_GET_CONST(float, attr_iter->second)));
break;
case proto::AttrType::FLOAT64:
phi_kernel_context->EmplaceBackAttr(
phi::Scalar(PADDLE_GET_CONST(double, attr_iter->second)));
break;
case proto::AttrType::INT:
phi_kernel_context->EmplaceBackAttr(
phi::Scalar(PADDLE_GET_CONST(int, attr_iter->second)));
break;
case proto::AttrType::LONG:
phi_kernel_context->EmplaceBackAttr(
phi::Scalar(PADDLE_GET_CONST(int64_t, attr_iter->second)));
break;
case proto::AttrType::STRING:
phi_kernel_context->EmplaceBackAttr(phi::Scalar(
PADDLE_GET_CONST(std::string, attr_iter->second)));
break;
case proto::AttrType::BOOLEAN:
phi_kernel_context->EmplaceBackAttr(
phi::Scalar(PADDLE_GET_CONST(bool, attr_iter->second)));
break;
case proto::AttrType::SCALAR:
phi_kernel_context->EmplaceBackAttr(phi::Scalar(PADDLE_GET_CONST(
paddle::experimental::Scalar, attr_iter->second)));
break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` to Scalar when construct "
"KernelContext in dygraph.",
attr_names[i]));
}
} else { // scalar is in the input
need_prepare_phi_data_ = true;
auto& ins_vector = ctx.inputs.at(attr_names[i]);
phi_kernel_context->EmplaceBackAttr(
framework::MakePhiScalarFromVar(*ins_vector.front()));
}
break;
case phi::AttributeType::INT_ARRAY:
if (attr_iter != Attrs().end()) {
switch (AttrTypeID(attr_iter->second)) {
case proto::AttrType::INTS: // NOLINT
phi_kernel_context->EmplaceBackAttr(phi::IntArray(
PADDLE_GET_CONST(std::vector<int32_t>, attr_iter->second)));
break;
case proto::AttrType::LONGS:
phi_kernel_context->EmplaceBackAttr(phi::IntArray(
PADDLE_GET_CONST(std::vector<int64_t>, attr_iter->second)));
break;
case proto::AttrType::INT:
phi_kernel_context->EmplaceBackAttr(phi::IntArray(
&PADDLE_GET_CONST(int32_t, attr_iter->second), 1));
break;
case proto::AttrType::LONG:
phi_kernel_context->EmplaceBackAttr(phi::IntArray(
&PADDLE_GET_CONST(int64_t, attr_iter->second), 1));
break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` to IntArray when "
"construct KernelContext.",
attr_names[i]));
}
} else { // shape is in the input
need_prepare_phi_data_ = true;
auto& ins_vector = ctx.inputs.at(attr_names[i]);
if (ins_vector.size() == 1) { // ShapeTensor
phi_kernel_context->EmplaceBackAttr(
framework::MakePhiIntArrayFromVar(*ins_vector.front()));
} else { // ShapeTensorList
phi_kernel_context->EmplaceBackAttr(
framework::MakePhiIntArrayFromVarList(ins_vector));
}
}
break;
case phi::AttributeType::SCALARS: {
PADDLE_ENFORCE_NE(
attr_iter,
Attrs().end(),
common::errors::NotFound("(%s) is not found in AttributeMap when "
"building static KernelContext.",
attr_names[i]));
switch (AttrTypeID(attr_iter->second)) {
case proto::AttrType::INTS: {
const auto& vec =
PADDLE_GET_CONST(std::vector<int32_t>, attr_iter->second);
std::vector<phi::Scalar> scalar_list;
scalar_list.reserve(vec.size());
for (const auto& val : vec) {
scalar_list.emplace_back(val);
}
phi_kernel_context->EmplaceBackAttr(std::move(scalar_list));
} break;
case proto::AttrType::LONGS: {
const auto& vec =
PADDLE_GET_CONST(std::vector<int64_t>, attr_iter->second);
std::vector<phi::Scalar> scalar_list;
scalar_list.reserve(vec.size());
for (const auto& val : vec) {
scalar_list.emplace_back(val);
}
phi_kernel_context->EmplaceBackAttr(std::move(scalar_list));
} break;
case proto::AttrType::FLOATS: {
const auto& vec =
PADDLE_GET_CONST(std::vector<float>, attr_iter->second);
std::vector<phi::Scalar> scalar_list;
scalar_list.reserve(vec.size());
for (const auto& val : vec) {
scalar_list.emplace_back(val);
}
phi_kernel_context->EmplaceBackAttr(std::move(scalar_list));
} break;
case proto::AttrType::FLOAT64S: {
const auto& vec =
PADDLE_GET_CONST(std::vector<double>, attr_iter->second);
std::vector<phi::Scalar> scalar_list;
scalar_list.reserve(vec.size());
for (const auto& val : vec) {
scalar_list.emplace_back(val);
}
phi_kernel_context->EmplaceBackAttr(std::move(scalar_list));
} break;
case proto::AttrType::BOOLEANS: {
const auto& vec =
PADDLE_GET_CONST(std::vector<bool>, attr_iter->second);
std::vector<phi::Scalar> scalar_list;
scalar_list.reserve(vec.size());
for (const auto& val : vec) {
scalar_list.emplace_back(val);
}
phi_kernel_context->EmplaceBackAttr(std::move(scalar_list));
} break;
case proto::AttrType::SCALARS: {
const auto& vec = PADDLE_GET_CONST(
std::vector<paddle::experimental::Scalar>, attr_iter->second);
std::vector<phi::Scalar> scalar_list{vec.begin(), vec.end()};
phi_kernel_context->EmplaceBackAttr(std::move(scalar_list));
} break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` to vector<Scalar> when "
"construct KernelContext.",
attr_names[i]));
}
} break;
default: {
if (attr_iter == Attrs().end()) {
// TODO(chenweihang): remove this backup searching later
attr_iter = RuntimeAttrs().find(attr_names[i]);
PADDLE_ENFORCE_NE(
attr_iter,
RuntimeAttrs().end(),
common::errors::NotFound("(%s) is not found in AttributeMap when "
"building static KernelContext.",
attr_names[i]));
}
switch (attr_defs[i].type_index) {
case phi::AttributeType::FLOAT32:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(float, attr_iter->second));
break;
case phi::AttributeType::FLOAT64:
if (AttrTypeID(attr_iter->second) ==
framework::proto::AttrType::FLOAT) {
const auto val = PADDLE_GET_CONST(float, attr_iter->second);
phi_kernel_context->EmplaceBackAttr(static_cast<double>(val));
break;
}
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(double, attr_iter->second));
break;
case phi::AttributeType::INT32:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(int, attr_iter->second));
break;
case phi::AttributeType::BOOL:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(bool, attr_iter->second));
break;
case phi::AttributeType::INT64:
switch (AttrTypeID(attr_iter->second)) {
case proto::AttrType::LONG:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(int64_t, attr_iter->second));
break;
case proto::AttrType::INT: {
const auto val = PADDLE_GET_CONST(int, attr_iter->second);
phi_kernel_context->EmplaceBackAttr(static_cast<int64_t>(val));
} break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` to int64_t when "
"construct KernelContext.",
attr_names[i]));
}
break;
case phi::AttributeType::INT32S: // NOLINT
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::vector<int>, attr_iter->second));
break;
case phi::AttributeType::BOOLS:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::vector<bool>, attr_iter->second));
break;
case phi::AttributeType::DATA_TYPE: {
auto data_type = phi::TransToPhiDataType(
static_cast<framework::proto::VarType::Type>(
PADDLE_GET_CONST(int, attr_iter->second)));
phi_kernel_context->EmplaceBackAttr(data_type);
} break;
case phi::AttributeType::STRING:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::string, attr_iter->second));
break;
case phi::AttributeType::INT64S:
switch (AttrTypeID(attr_iter->second)) {
case proto::AttrType::LONGS:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::vector<int64_t>, attr_iter->second));
break;
case proto::AttrType::INTS: {
const auto& vector_int_attr =
PADDLE_GET_CONST(std::vector<int>, attr_iter->second);
const std::vector<int64_t> vector_int64_attr(
vector_int_attr.begin(), vector_int_attr.end());
phi_kernel_context->EmplaceBackAttr(vector_int64_attr);
} break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` to vector<int64_t> "
"when "
"construct KernelContext.",
attr_names[i]));
}
break;
case phi::AttributeType::FLOAT32S:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::vector<float>, attr_iter->second));
break;
case phi::AttributeType::FLOAT64S:
switch (AttrTypeID(attr_iter->second)) {
case proto::AttrType::FLOAT64S:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::vector<double>, attr_iter->second));
break;
case proto::AttrType::FLOATS: {
const auto& vector_float_attr =
PADDLE_GET_CONST(std::vector<float>, attr_iter->second);
const std::vector<double> vector_double_attr(
vector_float_attr.begin(), vector_float_attr.end());
phi_kernel_context->EmplaceBackAttr(vector_double_attr);
} break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` to vector<int64_t> "
"when "
"construct KernelContext.",
attr_names[i]));
}
break;
case phi::AttributeType::STRINGS:
phi_kernel_context->EmplaceBackAttr(
PADDLE_GET_CONST(std::vector<std::string>, attr_iter->second));
break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported cast op attribute `%s` when construct "
"KernelContext in dygraph.",
attr_names[i]));
}
}
}
}
VLOG(4) << "Done attributes";
// Clear All old attrs before add new attrs,
// because sometimes old attrs may be misused.
#if defined(PADDLE_WITH_DNNL)
if (phi::OneDNNContext::classof(dev_ctx)) {
phi::OneDNNContext* one_dnn_ctx = static_cast<phi::OneDNNContext*>(dev_ctx);
one_dnn_ctx->ClearDnnAttr();
if (!RuntimeAttrs().empty()) need_prepare_phi_data_ = true;
}
#endif
// Note(YuanRisheng): Now, we can't open code below.
// Because some unittest run OLD dygraph and ExtraAttr is not supported in OLD
// dygraph. So, here we use trick that dev_ctx is a global object. We can
// store ExtraAttr in static graph and when unittest run OLD dygraph, it can
// obtain these ExtraAttr. We can open this code when OLD dygraph is no longer
// used.
/*
#if defined(PADDLE_WITH_CUDA)
if(phi::GPUContext::classof(dev_ctx)) {
phi::GPUContext* gpu_dnn_ctx = static_cast<phi::GPUContext*>(dev_ctx);
gpu_dnn_ctx->ClearDnnAttr();
}
#endif
*/
// For compatible with Op with extra attrs for specific backend
#if defined(PADDLE_WITH_DNNL) || defined(PADDLE_WITH_CUDA) || \
defined(PADDLE_WITH_CUSTOM_DEVICE)
auto& runtime_attrs = RuntimeAttrs();
for (const auto& attr_iter : runtime_attrs) {
auto& attr_name = attr_iter.first;
auto& attr = attr_iter.second;
auto attr_properties = paddle::operators::GetExtraAttrProperties(attr_name);
SetDnnAttrIntoDeviceContext(dev_ctx, attr, attr_name, attr_properties);
}
// TODO(chenweihang): Since the pass will still `SetAttr` in the OpDesc,
// we try to add these Attrs to the RuntimeAttrs, but these OpDesc will lose
// the RuntimeAttrs information in the process of converting the Graph to
// the Program, so additional record configuration will be introduced,
// which increases the cost of development and understanding, so we
// still use Attrs to get and the attributes set by these passes from Attrs
// for the time being. In the future, it is necessary to clarify the
// positioning of RuntimeAttrs and expand related functions.
auto& attrs = Attrs();
for (const auto& attr_iter : attrs) {
auto& attr_name = attr_iter.first;
auto& attr = attr_iter.second;
auto attr_properties = paddle::operators::GetExtraAttrProperties(attr_name);
SetDnnAttrIntoDeviceContext(dev_ctx, attr, attr_name, attr_properties);
}
VLOG(4) << "Done runtime attributes";
#endif
// For compatible with Op with extra input for onednn backend
#ifdef PADDLE_WITH_DNNL
if (phi::OneDNNContext::classof(dev_ctx)) {
phi::OneDNNContext* one_dnn_ctx = static_cast<phi::OneDNNContext*>(dev_ctx);
auto& extra_input_names =
paddle::operators::ExtraInfoUtils::Instance().GetExtraInputNamesMap(
Type());
for (const auto& input_name : extra_input_names) {
auto it = ctx.inputs.find(input_name);
if (it == ctx.inputs.end() || it->second.empty()) {
one_dnn_ctx->SetDnnInput(input_name, nullptr);
} else {
auto ins_vector = it->second;
PADDLE_ENFORCE_EQ(
ins_vector.size(),
1UL,
common::errors::InvalidArgument(
"OneDNN's extra input only allows one input tensor."));
auto* var = ins_vector[0];
PADDLE_ENFORCE_EQ(var->IsType<DenseTensor>(),
true,
common::errors::InvalidArgument(
"OneDNN's extra input only can be DenseTensor."));
one_dnn_ctx->SetDnnInput(input_name, &(var->Get<DenseTensor>()));
}
}
}
VLOG(4) << "Done runtime extra inputs";
#endif
}
} // namespace paddle::framework