From a9608f6067b1d6a899c0a3f625e68325097caab0 Mon Sep 17 00:00:00 2001 From: Zhanlue Yang Date: Fri, 26 Nov 2021 10:12:09 +0800 Subject: [PATCH] Added fluid dependencies to Eager Dygraph (#37555) --- paddle/fluid/eager/legacy/CMakeLists.txt | 2 + paddle/fluid/eager/legacy/amp_auto_cast.cc | 258 +++++++++++ paddle/fluid/eager/legacy/amp_auto_cast.h | 95 ++++ paddle/fluid/eager/legacy/execution_context.h | 212 +++++++++ .../fluid/eager/legacy/infer_shape_context.h | 404 ++++++++++++++++++ 5 files changed, 971 insertions(+) create mode 100644 paddle/fluid/eager/legacy/CMakeLists.txt create mode 100644 paddle/fluid/eager/legacy/amp_auto_cast.cc create mode 100644 paddle/fluid/eager/legacy/amp_auto_cast.h create mode 100644 paddle/fluid/eager/legacy/execution_context.h create mode 100644 paddle/fluid/eager/legacy/infer_shape_context.h diff --git a/paddle/fluid/eager/legacy/CMakeLists.txt b/paddle/fluid/eager/legacy/CMakeLists.txt new file mode 100644 index 0000000000000..ac3a9af6d1477 --- /dev/null +++ b/paddle/fluid/eager/legacy/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB DYGRAPH_LEGACY "*.cpp" "*.cc") +set(DYGRAPH_LEGACY ${DYGRAPH_LEGACY} PARENT_SCOPE) diff --git a/paddle/fluid/eager/legacy/amp_auto_cast.cc b/paddle/fluid/eager/legacy/amp_auto_cast.cc new file mode 100644 index 0000000000000..b86cb7a48f616 --- /dev/null +++ b/paddle/fluid/eager/legacy/amp_auto_cast.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2020 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/eager/legacy/amp_auto_cast.h" +#include +#include +#include "paddle/fluid/eager/legacy/op_runner.h" +#include "paddle/fluid/eager/legacy/tensor_helper.h" +#include "paddle/fluid/framework/operator.h" + +namespace egr { + +AmpOperators::AmpOperators() + : allow_ops_(new std::unordered_set()), + block_ops_(new std::unordered_set()), + unsupported_fp16_ops_(new std::unordered_set()) { + auto& all_kernels = paddle::framework::OperatorWithKernel::AllOpKernels(); + auto fp16_dtype = paddle::framework::proto::VarType::FP16; + for (auto it = all_kernels.begin(); it != all_kernels.end(); it++) { + bool supported = false; + for (auto& kernel_type : it->second) { + if ((paddle::platform::is_gpu_place(kernel_type.first.place_) || + paddle::platform::is_xpu_place(kernel_type.first.place_)) && + kernel_type.first.data_type_ == fp16_dtype) { + supported = true; + } + } + if (!supported) { + unsupported_fp16_ops_->insert(it->first); + } + } +} + +AmpOperators::~AmpOperators() {} + +AmpOperators& AmpOperators::Instance() { + static AmpOperators instance; + return instance; +} + +std::shared_ptr> +AmpOperators::GetMutableAllowOps() { + return allow_ops_; +} + +std::shared_ptr> +AmpOperators::GetMutableBlockOps() { + return block_ops_; +} + +std::shared_ptr> +AmpOperators::GetMutableUnsupportedFp16Ops() { + return unsupported_fp16_ops_; +} + +std::ostream& operator<<(std::ostream& os, AmpOperators& ops) { + os << "allow ops: "; + auto allow_ops = ops.GetMutableAllowOps(); + std::copy((*allow_ops).begin(), (*allow_ops).end(), + std::ostream_iterator(os, " ")); + os << "\n"; + os << "block ops: "; + auto block_ops = ops.GetMutableBlockOps(); + std::copy((*block_ops).begin(), (*block_ops).end(), + std::ostream_iterator(os, " ")); + os << "\n"; + os << "unsupported fp16 ops: "; + auto unsupported_fp16_ops = ops.GetMutableUnsupportedFp16Ops(); + std::copy((*unsupported_fp16_ops).begin(), (*unsupported_fp16_ops).end(), + std::ostream_iterator(os, " ")); + return os; +} + +inline std::string GetDtypeStr( + const std::shared_ptr& tensor) { + return paddle::framework::DataTypeToString( + egr::GetDtypeFromVar(tensor->Var())); +} + +inline bool NeedCast(const std::shared_ptr& tensor) { + auto place = egr::GetPlaceFromVar(tensor->Var()); + auto data_type = egr::GetDtypeFromVar(tensor->Var()); + if (paddle::platform::is_gpu_place(place) || + paddle::platform::is_cuda_pinned_place(place) || + paddle::platform::is_xpu_place(place)) { + // CudaPinndePlace is added for varbase created by dataloader + if (data_type == paddle::framework::proto::VarType::FP32 || + data_type == paddle::framework::proto::VarType::FP16) { + return true; + } + } + return false; +} + +// NOTE: Trace a cast op, so if a var is casted from fp32 to fp16, then the grad +// var will be cast back from fp16 to fp32 during backward phase. +static inline std::shared_ptr CastToType( + const std::shared_ptr& tensor, + const paddle::framework::proto::VarType::Type dst_type) { + NameTensorMap ins = {{"X", {tensor}}}; + auto in_data_type = egr::GetDtypeFromVar(tensor->Var()); + paddle::framework::AttributeMap attrs = {{"in_dtype", in_data_type}, + {"out_dtype", dst_type}}; + auto out = std::shared_ptr(new egr::EagerTensor()); + NameTensorMap outs = {{"Out", {out}}}; + + { + AutoCastGuard guard(0); + paddle::framework::AttributeMap default_attrs; + RunOp("cast", ins, outs, std::move(attrs), {}, &default_attrs, true); + } + + return out; +} + +static inline std::shared_ptr CastToFP16( + const std::shared_ptr& tensor) { + auto dst_type = paddle::framework::proto::VarType::FP16; + if (NeedCast(tensor) && (egr::GetDtypeFromVar(tensor->Var()) != dst_type)) { + return CastToType(tensor, dst_type); + } + return tensor; +} + +static inline std::shared_ptr CastToFP32( + const std::shared_ptr& tensor) { + auto dst_type = paddle::framework::proto::VarType::FP32; + if (NeedCast(tensor) && (egr::GetDtypeFromVar(tensor->Var()) != dst_type)) { + return CastToType(tensor, dst_type); + } + return tensor; +} + +static inline paddle::framework::proto::VarType::Type GetPromoteType( + const std::string& op_type, const NameTensorMap& ins) { + auto dst_type = paddle::framework::proto::VarType::FP16; + for (const auto& pair : ins) { + for (const auto& tensor : pair.second) { + if (egr::GetDtypeFromVar(tensor->Var()) == + paddle::framework::proto::VarType::FP32) { + dst_type = egr::GetDtypeFromVar(tensor->Var()); + break; + } + } + } + + // NOTE(juncai): moving_average_abs_max_scale only consider the + // dtype of input(X) + if (op_type == "moving_average_abs_max_scale") { + for (const auto& pair : ins) { + if (pair.first == "X" && + egr::GetDtypeFromVar(pair.second.front()->Var()) == + paddle::framework::proto::VarType::FP16) { + dst_type = paddle::framework::proto::VarType::FP16; + } + } + } + + return dst_type; +} + +NameTensorMap AutoCastInputs(const std::string& op_type, + const NameTensorMap& ins) { + NameTensorMap new_ins(ins); + if (AmpOperators::Instance().GetMutableAllowOps()->count(op_type)) { + for (auto& pair : new_ins) { + // NOTE(zhiqiu): batch_norm and layer_norm support only input x is fp16. + if ((op_type == "batch_norm" || op_type == "layer_norm" || + op_type == "sync_batch_norm") && + pair.first != "X") { + continue; + } + + VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from " + << GetDtypeStr(*pair.second.cbegin()) << " to float16"; + for (auto& var : pair.second) { + var = CastToFP16(var); + } + } + return new_ins; + } else if (AmpOperators::Instance().GetMutableBlockOps()->count(op_type)) { + for (auto& pair : new_ins) { + VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from " + << GetDtypeStr(*pair.second.cbegin()) << " to float"; + for (auto& var : pair.second) { + var = CastToFP32(var); + } + } + return new_ins; + } else { + auto dst_type = GetPromoteType(op_type, ins); + + // NOTE(zhiqiu): if the op has op fp16 kernel, fall back to fp32. + if (dst_type == paddle::framework::proto::VarType::FP16 && + AmpOperators::Instance().GetMutableUnsupportedFp16Ops()->count( + op_type)) { + dst_type = paddle::framework::proto::VarType::FP32; + } + for (auto& pair : new_ins) { + // NOTE(zhiqiu): batch_norm and layer_norm support only input x is fp16. + if ((op_type == "batch_norm" || op_type == "layer_norm" || + op_type == "sync_batch_norm") && + pair.first == "X" && + dst_type == paddle::framework::proto::VarType::FP32) { + continue; + } + VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from " + << GetDtypeStr(*pair.second.cbegin()) << " to " + << paddle::framework::DataTypeToString(dst_type); + for (auto& var : pair.second) { + var = (dst_type == paddle::framework::proto::VarType::FP32 + ? CastToFP32(var) + : CastToFP16(var)); + } + } + return new_ins; + } + return new_ins; +} + +NameTensorMap CastPureFp16Inputs(const std::string& op_type, + const NameTensorMap& ins) { + NameTensorMap new_ins(ins); + auto dst_type = paddle::framework::proto::VarType::FP16; + if (AmpOperators::Instance().GetMutableUnsupportedFp16Ops()->count(op_type) || + AmpOperators::Instance().GetMutableBlockOps()->count(op_type)) { + dst_type = paddle::framework::proto::VarType::FP32; + } + for (auto& pair : new_ins) { + if ((op_type == "batch_norm" || op_type == "layer_norm" || + op_type == "sync_batch_norm") && + pair.first != "X") { + continue; + } + VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from " + << GetDtypeStr(*pair.second.cbegin()) << " to " + << paddle::framework::DataTypeToString(dst_type); + for (auto& var : pair.second) { + var = (dst_type == paddle::framework::proto::VarType::FP32 + ? CastToFP32(var) + : CastToFP16(var)); + } + } + return new_ins; +} + +} // namespace egr diff --git a/paddle/fluid/eager/legacy/amp_auto_cast.h b/paddle/fluid/eager/legacy/amp_auto_cast.h new file mode 100644 index 0000000000000..c4c1b6d352f1d --- /dev/null +++ b/paddle/fluid/eager/legacy/amp_auto_cast.h @@ -0,0 +1,95 @@ +// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include +#include +#include +#include + +#include "paddle/fluid/eager/api/utils/global_utils.h" +#include "paddle/fluid/eager/eager_tensor.h" +#include "paddle/fluid/eager/legacy/type_def.h" + +namespace egr { + +// NOTE(zhiqiu): only O1 and O2 are valid now +enum class AmpLevel { + O0 = 0, // fp32 + O1, // amp, mixed fp32-fp16 + O2, // almost fp16 + O3, // fp16 +}; + +class AmpOperators { + public: + ~AmpOperators(); + AmpOperators(const AmpOperators& o) = delete; + const AmpOperators& operator=(const AmpOperators& o) = delete; + + static AmpOperators& Instance(); + + std::shared_ptr> GetMutableAllowOps(); + + std::shared_ptr> GetMutableBlockOps(); + + std::shared_ptr> + GetMutableUnsupportedFp16Ops(); + + private: + AmpOperators(); // forbid calling default constructor + + // The set of ops that support fp16 calculation and are considered numerically + // safe and performance critical. These ops are always converted to fp16. + std::shared_ptr> allow_ops_; + + // The set of ops that support fp16 calculation and are considered numerically + // dangerous and whose effects may also be observed in downstream ops. + std::shared_ptr> block_ops_; + + // The set of ops that has no fp16 CUDA kennel. + std::shared_ptr> unsupported_fp16_ops_; +}; + +std::ostream& operator<<(std::ostream& os, AmpOperators& ops); + +// NOTE(zhiqiu): AutoCastGuard is used for RAII. +class AutoCastGuard { + public: + explicit AutoCastGuard(int guard_level) { + pre_amp_level_ = Controller::Instance().GetAMPLevel(); + + if (pre_amp_level_ != guard_level) { + Controller::Instance().SetAMPLevel(guard_level); + } + } + + ~AutoCastGuard() { Controller::Instance().SetAMPLevel(pre_amp_level_); } + + // forbid copy and operator= + AutoCastGuard(const AutoCastGuard& guard) = delete; + AutoCastGuard& operator=(const AutoCastGuard& guard) = delete; + + private: + int pre_amp_level_; +}; + +NameTensorMap AutoCastInputs(const std::string& op_type, + const NameTensorMap& ins); + +NameTensorMap CastPureFp16Inputs(const std::string& op_type, + const NameTensorMap& ins); + +} // namespace egr diff --git a/paddle/fluid/eager/legacy/execution_context.h b/paddle/fluid/eager/legacy/execution_context.h new file mode 100644 index 0000000000000..ad252ecb89a19 --- /dev/null +++ b/paddle/fluid/eager/legacy/execution_context.h @@ -0,0 +1,212 @@ +// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include "paddle/fluid/eager/eager_tensor.h" +#include "paddle/fluid/eager/legacy/type_def.h" +#include "paddle/fluid/framework/operator.h" +#include "paddle/fluid/framework/type_defs.h" +#include "paddle/fluid/framework/variable.h" +namespace egr { + +class EagerExecutionContext : public paddle::framework::ExecutionContext { + using Variable = paddle::framework::Variable; + + public: + EagerExecutionContext(const paddle::framework::OperatorBase& op, + const paddle::framework::Scope& scope, + const paddle::platform::DeviceContext& device_context, + const paddle::framework::RuntimeContext& ctx, + const NameTensorMap& tensor_map_in, + const NameTensorMap& tensor_map_out, + const paddle::framework::AttributeMap& attrs, + const paddle::framework::AttributeMap& default_attrs) + : ExecutionContext(op, scope, device_context, ctx), + tensor_map_in_(tensor_map_in), + tensor_map_out_(tensor_map_out), + attrs_(attrs), + default_attrs_(default_attrs) {} + + std::string InputName(const std::string& name) const override { + auto it = tensor_map_in_.find(name); + PADDLE_ENFORCE_NE(it, tensor_map_in_.end(), + paddle::platform::errors::PreconditionNotMet( + "Can not find [%s] in Input", name)); + // TODO(jiabin): This is used for egr::EagerTensor temporally, + // once we have name, remove it. + return it->second[0] ? it->second[0]->name() + : paddle::framework::kEmptyVarName; + } + + std::vector InputNames(const std::string& name) const override { + auto it = tensor_map_in_.find(name); + PADDLE_ENFORCE_NE( + it, tensor_map_in_.end(), + paddle::platform::errors::NotFound("Can not find [%s] in Input", name)); + std::vector vec_res; + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + if (it->second[i]) { + // TODO(jiabin): This is used for egr::EagerTensor + // temporally, once we have name, remove it. + vec_res.push_back(it->second[i]->name()); + } else { + vec_res.push_back(paddle::framework::kEmptyVarName); + } + } + return vec_res; + } + + std::string OutputName(const std::string& name) const override { + auto it = tensor_map_out_.find(name); + PADDLE_ENFORCE_NE(it, tensor_map_out_.end(), + paddle::platform::errors::NotFound( + "Can not find [%s] in Output", name)); + return it->second[0] ? it->second[0]->name() + : paddle::framework::kEmptyVarName; + } + + std::vector OutputNames(const std::string& name) const override { + auto it = tensor_map_out_.find(name); + PADDLE_ENFORCE_NE(it, tensor_map_out_.end(), + paddle::platform::errors::NotFound( + "Can not find [%s] in Output", name)); + std::vector vec_res; + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + if (it->second[i]) { + vec_res.push_back(it->second[i]->name()); + } else { + vec_res.push_back(paddle::framework::kEmptyVarName); + } + } + return vec_res; + } + + bool HasAttr(const std::string& name) const override { + return attrs_.count(name) != 0 || default_attrs_.count(name) != 0; + } + + const paddle::framework::AttributeMap& Attrs() const override { + return attrs_; + } + + const paddle::framework::Attribute& GetAttr( + const std::string& name) const override { + auto it = attrs_.find(name); + + if (it == attrs_.end()) { + it = default_attrs_.find(name); + if (it == default_attrs_.end()) { + PADDLE_THROW(paddle::platform::errors::NotFound( + "Can not find [%s] in attributes of op %s.", name, + this->GetOp().Type())); + } + } + + return it->second; + } + + std::vector InNameList() const override { + std::vector vec_temp; + vec_temp.reserve(tensor_map_in_.size()); + + for (auto& v : tensor_map_in_) { + vec_temp.push_back(v.first); + } + + return vec_temp; + } + + bool HasInput(const std::string& name) const override { + auto it = tensor_map_in_.find(name); + return (it != tensor_map_in_.end() && it->second.size() > 0); + } + + bool HasOutput(const std::string& name) const override { + auto it = tensor_map_out_.find(name); + return (it != tensor_map_out_.end() && it->second.size() > 0); + } + + size_t InputSize(const std::string& name) const override { + return InputNames(name).size(); + } + + size_t OutputSize(const std::string& name) const override { + return OutputNames(name).size(); + } + + const Variable* InputVar(const std::string& name) const override { + auto it = tensor_map_in_.find(name); + if (it == tensor_map_in_.end()) { + return nullptr; + } + + return it->second.empty() || it->second[0] == nullptr + ? nullptr + : it->second[0]->MutableVar(); + } + + Variable* OutputVar(const std::string& name) const override { + auto it = tensor_map_out_.find(name); + if (it == tensor_map_out_.end()) { + return nullptr; + } + + return it->second.empty() || it->second[0] == nullptr + ? nullptr + : it->second[0]->MutableVar(); + } + + const std::vector MultiInputVar( + const std::string& name) const override { + auto it = tensor_map_in_.find(name); + if (it == tensor_map_in_.end()) { + return {}; + } + std::vector vec_res; + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + vec_res.push_back(it->second[i] ? it->second[i]->MutableVar() : nullptr); + } + + return vec_res; + } + + std::vector MultiOutputVar( + const std::string& name) const override { + auto it = tensor_map_out_.find(name); + if (it == tensor_map_out_.end()) { + return {}; + } + std::vector vec_res; + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + vec_res.push_back(it->second[i] ? it->second[i]->MutableVar() : nullptr); + } + + return vec_res; + } + + private: + const NameTensorMap& tensor_map_in_; + const NameTensorMap& tensor_map_out_; + const paddle::framework::AttributeMap& attrs_; + const paddle::framework::AttributeMap& default_attrs_; +}; + +} // namespace egr diff --git a/paddle/fluid/eager/legacy/infer_shape_context.h b/paddle/fluid/eager/legacy/infer_shape_context.h new file mode 100644 index 0000000000000..993532b99b4aa --- /dev/null +++ b/paddle/fluid/eager/legacy/infer_shape_context.h @@ -0,0 +1,404 @@ +// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include "paddle/fluid/eager/eager_tensor.h" +#include "paddle/fluid/eager/legacy/type_def.h" +#include "paddle/fluid/framework/ddim.h" +#include "paddle/fluid/framework/operator.h" +#include "paddle/fluid/framework/shape_inference.h" +#include "paddle/fluid/framework/type_defs.h" +#include "paddle/fluid/framework/var_type.h" +namespace egr { + +class EagerInferShapeContext : public paddle::framework::InferShapeContext { + using DDim = paddle::framework::DDim; + + public: + EagerInferShapeContext(const NameTensorMap* in, const NameTensorMap* out, + const paddle::framework::AttributeMap* attr, + const paddle::framework::AttributeMap* default_attr, + const std::string op_type) + : tensor_in_(in), + tensor_out_(out), + attrs_(attr), + default_attrs_(default_attr), + op_type_(op_type) {} + + bool HasInput(const std::string& name) const override { + // has only one input + auto it = tensor_in_->find(name); + + if (it == tensor_in_->end()) { + return false; + } + const auto& in = it->second; + if (in.size() == 0) return false; + PADDLE_ENFORCE_EQ( + in.size(), 1UL, + paddle::platform::errors::PreconditionNotMet( + "Input %s should not have more than one inputs", name)); + return in[0] != nullptr; + } + + bool HasOutput(const std::string& name) const override { + // has only one output + auto it = tensor_out_->find(name); + if (it == tensor_out_->end()) { + return false; + } + const auto& out = it->second; + if (out.size() == 0) { + return false; + } + PADDLE_ENFORCE_EQ( + out.size(), 1UL, + paddle::platform::errors::PreconditionNotMet( + "Output %s should not have more than one outputs", name)); + return out[0] != nullptr; + } + + bool HasInputs(const std::string& name) const override { + auto it = tensor_in_->find(name); + if (it == tensor_in_->end() || it->second.empty()) { + return false; + } + for (auto& input : it->second) { + if (input == nullptr) { + return false; + } + } + return true; + } + + bool HasOutputs(const std::string& name) const override { + auto it = tensor_out_->find(name); + if (it == tensor_out_->end() || it->second.empty()) { + return false; + } + for (auto& output : it->second) { + if (output == nullptr) { + return false; + } + } + return true; + } + + paddle::framework::AttrReader Attrs() const override { + return paddle::framework::AttrReader(*attrs_, *default_attrs_); + } + + std::vector Inputs(const std::string& name) const override { + std::vector vec_res; + auto it = tensor_in_->find(name); + PADDLE_ENFORCE_NE( + it, tensor_in_->end(), + paddle::platform::errors::NotFound("can not find [%s] in input", name)); + + vec_res.reserve(it->second.size()); + for (auto& var : it->second) { + if (var) { + vec_res.push_back(var->name()); + } else { + vec_res.push_back(paddle::framework::kEmptyVarName); + } + } + + return vec_res; + } + + std::vector Outputs(const std::string& name) const override { + std::vector vec_res; + auto it = tensor_out_->find(name); + PADDLE_ENFORCE_NE(it, tensor_out_->end(), + paddle::platform::errors::NotFound( + "can not find [%s] in output", name)); + + vec_res.reserve(it->second.size()); + for (auto& var : it->second) { + if (var) { + vec_res.push_back(var->name()); + } else { + vec_res.push_back(paddle::framework::kEmptyVarName); + } + } + + return vec_res; + } + std::string GetInputNameByIdx(size_t idx) const override { + auto& op_proto = + paddle::framework::OpInfoMap::Instance().Get(op_type_).proto_; + PADDLE_ENFORCE_LT(idx, op_proto->inputs().size(), + paddle::platform::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()[idx].name(); + } + + std::string GetOutputNameByIdx(size_t idx) const override { + auto& op_proto = + paddle::framework::OpInfoMap::Instance().Get(op_type_).proto_; + PADDLE_ENFORCE_LT( + idx, op_proto->outputs().size(), + paddle::platform::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()[idx].name(); + } + + void ShareDim(const std::string& in, const std::string& out, size_t i = 0, + size_t j = 0) override { + auto in_it = tensor_in_->find(in); + auto out_it = tensor_out_->find(out); + PADDLE_ENFORCE_NE( + in_it, tensor_in_->end(), + paddle::platform::errors::NotFound("can not found [%s] in input", in)); + PADDLE_ENFORCE_GT(in_it->second.size(), i, + paddle::platform::errors::PreconditionNotMet( + "Inputs %s should have %llu argument", in, i)); + PADDLE_ENFORCE_NE( + out_it, tensor_out_->end(), + paddle::platform::errors::NotFound("can not found [%s] in input", in)); + PADDLE_ENFORCE_GT(out_it->second.size(), j, + paddle::platform::errors::PreconditionNotMet( + "Outputs %s should have %llu argument", out, j)); + + paddle::framework::Variable* in_var = in_it->second[i]->MutableVar(); + paddle::framework::Variable* out_var = out_it->second[j]->MutableVar(); + + PADDLE_ENFORCE_EQ(in_var->Type(), out_var->Type(), + paddle::platform::errors::PreconditionNotMet( + "The type of %s and %s is not the same.", in, out)); + + if (in_var->IsType()) { + auto& in_lod_tensor = in_var->Get(); + auto* out_lod_tensor = + out_var->GetMutable(); + out_lod_tensor->Resize(in_lod_tensor.dims()); + } else { + auto& in_sele_rows = in_var->Get(); + auto out_sele_rows = + out_var->GetMutable(); + 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()); + } + } + + void ShareAllLoD(const std::string& in, + const std::string& out) const override { + // do nothing + } + void ShareLoD(const std::string& in, const std::string& out, size_t i = 0, + size_t j = 0) const override { + // do nothing + } + + bool IsRuntime() const override { return true; } + + // TODO(paddle-dev): Can this be template? + std::vector GetInputVarPtrs( + const std::string& name) override { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "GetInputVarPtrs not support in dygraph runtime context")); + } + + std::vector GetOutputVarPtrs( + const std::string& name) override { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "GetOutputVarPtrs not support in dygraph runtime context")); + } + + DDim GetInputDim(const std::string& name) const override { + auto it = tensor_in_->find(name); + PADDLE_ENFORCE_NE( + it, tensor_in_->end(), + paddle::platform::errors::NotFound("can not find [%s] in input", name)); + PADDLE_ENFORCE_EQ( + it->second.size(), 1UL, + paddle::platform::errors::PreconditionNotMet( + "Input(%s) should hold one element, but now it holds %d", name, + it->second.size())); + return this->GetDim(it->second[0]->MutableVar()); + } + + std::vector GetInputsDim(const std::string& name) const override { + // const std::vector& vars = InputVars(name); + std::vector vec_res; + auto it = tensor_in_->find(name); + PADDLE_ENFORCE_NE(it, tensor_in_->end(), + paddle::platform::errors::NotFound( + "can not find [%s] in output", name)); + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + if (it->second[i]) { + vec_res.emplace_back(GetDim(it->second[i]->MutableVar())); + } else { + vec_res.emplace_back(); + } + } + + return vec_res; + } + + std::vector GetInputsVarType( + const std::string& name) const override { + std::vector vec_res; + auto it = tensor_in_->find(name); + PADDLE_ENFORCE_NE( + it, tensor_in_->end(), + paddle::platform::errors::NotFound("can not find [%s] in input", name)); + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + if (it->second[i]) { + vec_res.emplace_back( + paddle::framework::ToVarType(it->second[i]->MutableVar()->Type())); + } else { + vec_res.emplace_back(); + } + } + return vec_res; + } + + std::vector GetOutputsVarType( + const std::string& name) const override { + std::vector vec_res; + auto it = tensor_out_->find(name); + PADDLE_ENFORCE_NE(it, tensor_out_->end(), + paddle::platform::errors::NotFound( + "can not find [%s] in output", name)); + vec_res.reserve(it->second.size()); + for (size_t i = 0; i < it->second.size(); ++i) { + if (it->second[i]) { + vec_res.emplace_back( + paddle::framework::ToVarType(it->second[i]->MutableVar()->Type())); + } else { + vec_res.emplace_back( + static_cast(-1)); + } + } + return vec_res; + } + + void SetOutputDim(const std::string& name, const DDim& dim) override { + auto it = tensor_out_->find(name); + PADDLE_ENFORCE_NE(it, tensor_out_->end(), + paddle::platform::errors::NotFound( + "can not find [%s] in output", name)); + + if (it->second[0]) { + SetDim(it->second[0]->MutableVar(), dim); + } + } + + void SetOutputsDim(const std::string& name, + const std::vector& dims) override { + auto it = tensor_out_->find(name); + PADDLE_ENFORCE_NE(it, tensor_out_->end(), + paddle::platform::errors::NotFound( + "can not find [%s] in output", name)); + + PADDLE_ENFORCE_EQ(dims.size(), it->second.size(), + paddle::platform::errors::InvalidArgument( + "The number of dims is expected to be equal to the " + "number of Outputs(%s). But receieved: the number of " + "dims = %d, the number of Outputs(%s) = %d.", + name, dims.size(), name, it->second.size())); + + for (size_t i = 0; i < dims.size(); ++i) { + if (it->second[i]) { + SetDim(it->second[i]->MutableVar(), dims[i]); + } + } + } + + int32_t GetLoDLevel(const std::string& in, size_t i = 0) const override { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "GetLoDLevel function not support in dygraph mode")); + } + + void SetLoDLevel(const std::string& out, int32_t lod_level, + size_t j = 0) const override { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "SetLoDLevel function not support in dygraph mode")); + } + + protected: + DDim GetDim(paddle::framework::Variable* var) const { + PADDLE_ENFORCE_NOT_NULL(var, paddle::platform::errors::PreconditionNotMet( + "Input variable should not be null")); + if (var->IsType()) { + return var->Get().dims(); + } else if (var->IsType()) { + return var->Get().GetCompleteDims(); + } else { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "Only LoDTensor/SelectedRows support 'GetDim', but Variables " + "type_id is xx.")); + } + } + + std::vector GetRepeatedDims(const std::string& name) const override { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "GetRepeatedDims not support in dygraph runtime")); + } + + void SetDim(paddle::framework::Variable* var, const DDim& dim) { + if (var->IsType()) { + var->GetMutable()->Resize(dim); + } else if (var->IsType()) { + var->GetMutable()->set_height(dim[0]); + } else { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "Variable type_id %s, expect LoDTensor/SelectedRows.")); + } + } + + void SetDims(const std::vector& vars, + const std::vector& dims) { + size_t length = vars.size(); + PADDLE_ENFORCE_EQ( + length, dims.size(), + paddle::platform::errors::PreconditionNotMet( + "Vars number [%d] should be equal with dims number [%d]", length, + dims.size())); + for (size_t i = 0; i < length; ++i) { + if (vars[i] == nullptr) { + continue; + } + SetDim(vars[i], dims[i]); + } + } + + void SetRepeatedDims(const std::string& name, + const std::vector& dims) override { + PADDLE_THROW(paddle::platform::errors::PermissionDenied( + "SetRepeatedDims not support in dygraph runtime")); + } + + private: + const NameTensorMap* tensor_in_; + const NameTensorMap* tensor_out_; + const paddle::framework::AttributeMap* attrs_; + const paddle::framework::AttributeMap* default_attrs_; + const std::string op_type_; +}; + +} // namespace egr