Files
2026-07-13 12:40:42 +08:00

3798 lines
144 KiB
C++

// Copyright (c) 2021 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 <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_set>
#include "paddle/fluid/framework/op_info.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/framework/variable.h"
#include "paddle/fluid/pybind/eager_generator.h"
#include "paddle/fluid/pybind/pybind.h"
#include "paddle/utils/string/string_helper.h"
// phi
#include "paddle/phi/kernels/declarations.h"
#define NUM_CREATED_DUP_INPUTS 4
namespace paddle::framework {
// To handle append_op at python-level
std::unordered_map<std::string, std::vector<std::string>>
core_ops_legacy_returns_info = {};
std::unordered_map<std::string, std::vector<std::string>>
core_ops_legacy_args_info = {};
std::unordered_map<std::string, std::vector<std::string>>
core_ops_legacy_args_type_info = {};
/* --- Static maps to handle corner cases --- */
static std::unordered_map<std::string, paddle::framework::AttributeMap>
operators_with_attrs = {};
static std::unordered_set<std::string> ops_to_fill_zero_for_empty_grads = {
"split", "rnn"};
/* --- Black Ops list that's NO NEED to apply code generation --- */
static std::unordered_set<std::string> black_ops_list = {
"run_program",
"fused_gate_attention",
"fused_feedforward",
"fused_attention",
"fused_gemm_epilogue",
"fused_bias_dropout_residual_layer_norm",
"sparse_divide_scalar",
"sparse_scale"};
static std::string LegalizeVariableName(const std::string& var_name) {
std::string ret = var_name;
std::replace(ret.begin(), ret.end(), '-', '_'); // replace all '-' to '_'
std::replace(ret.begin(), ret.end(), '@', '_'); // replace all '-' to '_'
return ret;
}
static std::string LegalizeVarName(const std::string& var_name) {
std::string ret = var_name;
std::replace(ret.begin(), ret.end(), '@', '_'); // replace all '-' to '_'
return ret;
}
static std::string HandleDynamicGradAttributes(const std::string& fwd_op_type,
const std::string& attrs_name) {
std::string additional_grad_attrs_str = "";
if (fwd_op_type == "sum") {
const char* GRAD_ATTRS_TEMPLATE = " %s[\"%s\"] = %s;\n";
additional_grad_attrs_str = paddle::string::Sprintf(
GRAD_ATTRS_TEMPLATE, attrs_name, "scale", "float(1.0)");
additional_grad_attrs_str += paddle::string::Sprintf(
GRAD_ATTRS_TEMPLATE, attrs_name, "bias", "float(0.0f)");
additional_grad_attrs_str += paddle::string::Sprintf(
GRAD_ATTRS_TEMPLATE, attrs_name, "bias_after_scale", "bool(true)");
} else if (fwd_op_type == "scale") {
const char* GRAD_ATTRS_TEMPLATE = " %s[\"%s\"] = %s;\n";
additional_grad_attrs_str += paddle::string::Sprintf(
GRAD_ATTRS_TEMPLATE, attrs_name, "bias", "float(0.0f)");
additional_grad_attrs_str += paddle::string::Sprintf(
GRAD_ATTRS_TEMPLATE, attrs_name, "bias_after_scale", "bool(true)");
}
return additional_grad_attrs_str;
}
static void PrepareAttrMapForOps() {
// Handle "fused_elemwise_add_activation"
std::vector<std::string> functor_list = {"a", "b"};
operators_with_attrs["fused_elemwise_add_activation"] = {};
operators_with_attrs["fused_elemwise_add_activation"]["functor_list"] =
functor_list;
// Handle "fused_elemwise_activation"
operators_with_attrs["fused_elemwise_activation"] = {};
operators_with_attrs["fused_elemwise_activation"]["functor_list"] =
functor_list;
// Handle "reverse"
std::vector<int> axis = {0};
operators_with_attrs["reverse"] = {};
operators_with_attrs["reverse"]["axis"] = axis;
// Handle "flip"
operators_with_attrs["flip"] = {};
operators_with_attrs["flip"]["axis"] = axis;
// Handle "cast"
operators_with_attrs["cast"] = {};
operators_with_attrs["cast"]["out_dtype"] = 5;
operators_with_attrs["cast"]["in_dtype"] = 5;
// Handle "c_split"
operators_with_attrs["c_split"] = {};
operators_with_attrs["c_split"]["nranks"] = 1;
}
/* --- Helper Objects --- */
class ForwardGenerationInfo {
public:
ForwardGenerationInfo()
: fwd_inputs_name_pos_map_(),
fwd_outputs_name_pos_map_(),
in_vars_(),
out_vars_() {}
const std::string& GetOpType() const { return op_type_; }
void SetOpType(const std::string& op_type) { op_type_ = op_type; }
const std::unordered_map<std::string, size_t>& GetFwdInputsNamePosMap()
const {
return fwd_inputs_name_pos_map_;
}
std::unordered_map<std::string, size_t>* GetMutableFwdInputsNamePosMap() {
return &fwd_inputs_name_pos_map_;
}
const std::unordered_map<std::string, size_t>& GetFwdOutputsNamePosMap()
const {
return fwd_outputs_name_pos_map_;
}
std::unordered_map<std::string, size_t>* GetMutableFwdOutputsNamePosMap() {
return &fwd_outputs_name_pos_map_;
}
const std::vector<proto::OpProto::Var>& GetInVars() const { return in_vars_; }
std::vector<proto::OpProto::Var>* GetMutableInVars() { return &in_vars_; }
const std::vector<proto::OpProto::Var>& GetOutVars() const {
return out_vars_;
}
std::vector<proto::OpProto::Var>* GetMutableOutVars() { return &out_vars_; }
private:
std::string op_type_;
std::unordered_map<std::string, size_t> fwd_inputs_name_pos_map_;
std::unordered_map<std::string, size_t> fwd_outputs_name_pos_map_;
std::vector<proto::OpProto::Var> in_vars_;
std::vector<proto::OpProto::Var> out_vars_;
};
class GradNodeGenerationInfo {
class OpBaseGenerationInfo {
public:
const std::string& GetOpBaseType() const { return op_base_type_; }
void SetOpBaseType(const std::string& op_type) { op_base_type_ = op_type; }
const std::map<std::string, std::string>& GetGradOutsSlotnameMap() const {
return grad_outs_slotname_map_;
}
std::map<std::string, std::string>* GetMutableGradOutsSlotnameMap() {
return &grad_outs_slotname_map_;
}
const std::map<std::string, std::string>& GetGradInsFwdSlotnameMap() const {
return grad_ins_fwd_slotname_map_;
}
std::map<std::string, std::string>* GetMutableGradInsFwdSlotnameMap() {
return &grad_ins_fwd_slotname_map_;
}
const std::map<std::string, std::string>& GetGradInsGradSlotnameMap()
const {
return grad_ins_grad_slotname_map_;
}
std::map<std::string, std::string>* GetMutableGradInsGradSlotnameMap() {
return &grad_ins_grad_slotname_map_;
}
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
GetGradIns() const {
return grad_ins_;
}
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>*
GetMutableGradIns() {
return &grad_ins_;
}
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
GetGradOuts() const {
return grad_outs_;
}
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>*
GetMutableGradOuts() {
return &grad_outs_;
}
const paddle::framework::AttributeMap& GetGradAttrs() const {
return grad_attrs_;
}
paddle::framework::AttributeMap* GetMutableGradAttrs() {
return &grad_attrs_;
}
const std::unordered_set<std::string>& GetNoNeedBufferInputs() const {
return no_need_buffer_ins_;
}
std::unordered_set<std::string>* GetMutableNoNeedBufferInputs() {
return &no_need_buffer_ins_;
}
const std::unordered_map<std::string, std::string>& GetBackwardInplaceMap()
const {
return backward_inplace_map_;
}
std::unordered_map<std::string, std::string>*
GetMutableBackwardInplaceMap() {
return &backward_inplace_map_;
}
private:
std::string op_base_type_;
std::map<std::string, std::string> grad_outs_slotname_map_;
std::map<std::string, std::string> grad_ins_fwd_slotname_map_;
std::map<std::string, std::string> grad_ins_grad_slotname_map_;
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>
grad_ins_;
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>
grad_outs_;
paddle::framework::AttributeMap grad_attrs_;
std::unordered_set<std::string> no_need_buffer_ins_;
std::unordered_map<std::string, std::string> backward_inplace_map_;
};
public:
GradNodeGenerationInfo() : op_base_infos_() {}
const std::string& GetFwdOpType() const { return fwd_op_type_; }
void SetFwdOpType(const std::string& op_type) { fwd_op_type_ = op_type; }
bool GenerateForwardOnly() const { return generate_forward_only_; }
void SetGenerateForwardOnly(bool generate_forward_only) {
generate_forward_only_ = generate_forward_only;
}
const std::vector<OpBaseGenerationInfo>& GetOpBaseInfos() const {
return op_base_infos_;
}
std::vector<OpBaseGenerationInfo>* GetMutableOpBaseInfos() {
return &op_base_infos_;
}
private:
std::string fwd_op_type_;
bool generate_forward_only_ = false;
std::vector<OpBaseGenerationInfo> op_base_infos_;
};
/* --- Helper Functions --- */
static std::string AttrTypeToString(const proto::AttrType& type) {
std::string ret;
switch (type) {
case (proto::AttrType::INT): {
ret = "int";
break;
}
case (proto::AttrType::FLOAT): {
ret = "float";
break;
}
case (proto::AttrType::STRING): {
ret = "std::string&";
break;
}
case (proto::AttrType::INTS): {
ret = "std::vector<int>&";
break;
}
case (proto::AttrType::FLOATS): {
ret = "std::vector<float>&";
break;
}
case (proto::AttrType::STRINGS): {
ret = "std::vector<std::string>&";
break;
}
case (proto::AttrType::BOOLEAN): {
ret = "bool";
break;
}
case (proto::AttrType::BOOLEANS): {
ret = "std::vector<bool>&";
break;
}
case (proto::AttrType::LONG): {
ret = "int64_t";
break;
}
case (proto::AttrType::LONGS): {
ret = "std::vector<int64_t>&";
break;
}
case (proto::AttrType::BLOCK): {
ret = "paddle::framework::BlockDesc*";
break;
}
case (proto::AttrType::BLOCKS): {
ret = "std::vector<paddle::framework::BlockDesc*>&";
break;
}
case (proto::AttrType::FLOAT64S): {
ret = "std::vector<double>&";
break;
}
default: {
PADDLE_THROW(common::errors::Fatal(
"AttrType of type paddle::variant only supports specific data types."
"However, detected unrecognized AttrType: %d",
type));
}
}
return ret;
}
template <typename T, bool IsVector>
static typename std::enable_if<IsVector, std::string>::type GetAttrValue(
const framework::Attribute& attr) {
std::string val = "";
val += "{";
for (auto x : PADDLE_GET_CONST(std::vector<T>, attr)) {
val += std::to_string(x) + ",";
}
if (val.size() > 1) val.pop_back();
val += "}";
return val;
}
template <typename T, bool IsVector>
static typename std::enable_if<!IsVector, std::string>::type GetAttrValue(
const framework::Attribute& attr) {
return std::to_string(PADDLE_GET_CONST(T, attr));
}
static std::pair<std::string, std::string> GetAttrType(
const framework::Attribute& attr, bool is_arg) {
std::string ret = "";
std::string val = "";
size_t variant_pos = attr.index();
switch (variant_pos) {
case (1): {
ret = "int";
val = GetAttrValue<int, false>(attr);
break;
}
case (2): {
ret = "float";
val = GetAttrValue<float, false>(attr);
break;
}
case (3): {
ret = "std::string";
if (is_arg) ret += "&";
val = "\"" + PADDLE_GET_CONST(std::string, attr) + "\"";
break;
}
case (4): {
ret = "std::vector<int>";
if (is_arg) ret += "&";
val = GetAttrValue<int, true>(attr);
break;
}
case (5): {
ret = "std::vector<float>";
if (is_arg) ret += "&";
val = GetAttrValue<float, true>(attr);
break;
}
case (6): {
ret = "std::vector<std::string>";
if (is_arg) ret += "&";
val += "{";
for (auto const& x : PADDLE_GET_CONST(std::vector<std::string>, attr)) {
val += "\"" + x + "\"" + ",";
}
if (val.size() > 1) val.pop_back();
val += "};";
break;
}
case (7): {
ret = "bool";
val = GetAttrValue<bool, false>(attr);
break;
}
case (8): {
ret = "std::vector<bool>";
if (is_arg) ret += "&";
val = GetAttrValue<bool, true>(attr);
break;
}
case (9): {
ret = "BlockDesc*";
break;
}
case (10): {
ret = "int64_t";
val = GetAttrValue<int64_t, false>(attr);
break;
}
case (11): {
ret = "std::vector<BlockDesc*>";
if (is_arg) ret += "&";
break;
}
case (12): {
ret = "std::vector<int64_t>";
if (is_arg) ret += "&";
val = GetAttrValue<int64_t, true>(attr);
break;
}
case (13): {
ret = "std::vector<double>";
if (is_arg) ret += "&";
val = GetAttrValue<double, true>(attr);
break;
}
default: {
PADDLE_THROW(common::errors::Fatal(
"AttrType of type paddle::variant only supports specific data types."
"However, detected unrecognized AttrType: %d",
variant_pos));
}
}
return {ret, val};
}
static void SlotNameMatching(
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
grad_map,
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
fwd_ins,
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
fwd_outs,
std::map<std::string, std::string>* grad_fwd_slotname_map_ptr,
std::map<std::string, std::string>* grad_grad_slotname_map_ptr) {
std::map<std::string, std::string>& grad_fwd_slotname_map =
*grad_fwd_slotname_map_ptr;
std::map<std::string, std::string>& grad_grad_slotname_map =
*grad_grad_slotname_map_ptr;
for (const auto& iter : grad_map) {
const std::string& grad_slot_name = iter.first;
const std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>&
grad_vars = iter.second;
// Find matching fwd_slot_name
bool found_matching = false;
for (const std::shared_ptr<paddle::imperative::VariableWrapper>& grad_var :
grad_vars) {
for (const auto& fwd_iter : fwd_ins) {
const std::string& fwd_slot_name = fwd_iter.first;
const std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>&
fwd_vars = fwd_iter.second;
for (const std::shared_ptr<paddle::imperative::VariableWrapper>&
fwd_var : fwd_vars) {
if (grad_var == fwd_var) {
if (grad_fwd_slotname_map.count(grad_slot_name) &&
grad_fwd_slotname_map[grad_slot_name] != fwd_slot_name) {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"Detected mismatched slot names: "
"grad_slot_name %s matches both %s and %s fwd_slot_name",
grad_slot_name,
grad_fwd_slotname_map[grad_slot_name],
fwd_slot_name));
}
grad_fwd_slotname_map[grad_slot_name] = fwd_slot_name;
found_matching = true;
}
if (fwd_var->GetGradVar() && grad_var == fwd_var->GetGradVar()) {
if (grad_grad_slotname_map.count(grad_slot_name) &&
grad_grad_slotname_map[grad_slot_name] != fwd_slot_name) {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"grad_slot_name %s matches both %s and %s fwd_slot_name",
grad_slot_name,
grad_grad_slotname_map[grad_slot_name],
fwd_slot_name));
}
grad_grad_slotname_map[grad_slot_name] = fwd_slot_name;
found_matching = true;
}
}
}
for (const auto& fwd_iter : fwd_outs) {
const std::string& fwd_slot_name = fwd_iter.first;
const std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>&
fwd_vars = fwd_iter.second;
for (const std::shared_ptr<paddle::imperative::VariableWrapper>&
fwd_var : fwd_vars) {
if (grad_var == fwd_var) {
if (grad_fwd_slotname_map.count(grad_slot_name) &&
grad_fwd_slotname_map[grad_slot_name] != fwd_slot_name) {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names: "
"grad_slot_name %s matches both %s and %s fwd_slot_name",
grad_slot_name,
grad_fwd_slotname_map[grad_slot_name],
fwd_slot_name));
}
grad_fwd_slotname_map[grad_slot_name] = fwd_slot_name;
found_matching = true;
}
if (fwd_var->GetGradVar() && grad_var == fwd_var->GetGradVar()) {
if (grad_grad_slotname_map.count(grad_slot_name) &&
grad_grad_slotname_map[grad_slot_name] != fwd_slot_name) {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"grad_slot_name %s matches both %s and %s fwd_slot_name",
grad_slot_name,
grad_grad_slotname_map[grad_slot_name],
fwd_slot_name));
}
grad_grad_slotname_map[grad_slot_name] = fwd_slot_name;
found_matching = true;
}
}
}
}
if (!found_matching) {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"Found no matching fwd_slot_name for grad_slot_name: %s",
grad_slot_name));
} else {
std::string fwd_slot_name = grad_grad_slotname_map.count(grad_slot_name)
? grad_grad_slotname_map[grad_slot_name]
: grad_fwd_slotname_map[grad_slot_name];
VLOG(6) << "Found matching fwd_slot_name: " << fwd_slot_name
<< " for grad_slot_name: " << grad_slot_name;
}
}
}
static bool CheckOpProto(proto::OpProto* op_proto) {
if (op_proto == nullptr) {
return false;
}
const std::string& op_type = op_proto->type();
// Skip operator which is not inherit form OperatorWithKernel, like while,
// since only OperatorWithKernel can run in dygraph mode.
auto& all_kernels = paddle::framework::OperatorWithKernel::AllOpKernels();
if (!all_kernels.count(op_type) &&
!phi::KernelFactory::Instance().HasCompatiblePhiKernel(op_type)) {
return false;
}
// Only handle matmul_v2 for now
VLOG(3) << "------ Analyzing Op ------: " << op_type;
return true;
}
static bool BeSameAsInput(const std::string& output_name,
const std::set<std::string>& input_names) {
if (output_name.size() < 4) {
return false;
}
if (output_name.substr(output_name.size() - 3, 3) == "Out") {
if (input_names.count(output_name.substr(0, output_name.size() - 3))) {
return true;
}
}
return false;
}
/* --------------------------------------- */
/* --------- Preprocess Ins/Outs --------- */
/* --------------------------------------- */
static void PurifyForwardOpProto(const proto::OpProto& op_proto,
ForwardGenerationInfo* fwd_info) {
// Op Name
const std::string& op_name = op_proto.type();
auto* in_vars = fwd_info->GetMutableInVars();
auto* out_vars = fwd_info->GetMutableOutVars();
auto* fwd_inputs_name_pos_map = fwd_info->GetMutableFwdInputsNamePosMap();
auto* fwd_outputs_name_pos_map = fwd_info->GetMutableFwdOutputsNamePosMap();
// Handle dispensable inputs
for (const proto::OpProto::Var& input : op_proto.inputs()) {
std::string input_name = input.name();
// Delete dispensable tensor unless specified in op_ins_map
if (input.dispensable()) {
if (!op_ins_map.count(op_name) ||
!op_ins_map[op_name].count(input_name)) {
VLOG(6) << "Removing Dispensable Input: " << input_name;
// in_vars
auto iter = in_vars->begin();
for (iter = in_vars->begin(); iter != in_vars->end(); iter++) {
if (iter->name() == input_name) {
break;
}
}
in_vars->erase(iter);
}
}
}
for (const proto::OpProto::Var& output : op_proto.outputs()) {
std::string output_name = output.name();
// Delete dispensable tensor unless specified in op_outs_map
if (output.dispensable()) {
if (!op_outs_map.count(op_name) ||
!op_outs_map[op_name].count(output_name)) {
VLOG(6) << "Removing Dispensable Output: " << output_name;
// out_vars
auto iter = out_vars->begin();
for (iter = out_vars->begin(); iter != out_vars->end(); iter++) {
if (iter->name() == output_name) {
break;
}
}
out_vars->erase(iter);
}
}
}
/* ------ Mapping forward slot name to fwd position ------ */
size_t in_pos = 0;
for (const auto& var : *in_vars) {
VLOG(6) << "Mapping input tensor: " << var.name()
<< " To position: " << in_pos;
(*fwd_inputs_name_pos_map)[var.name()] = in_pos;
in_pos++;
}
size_t out_pos = 0;
for (const auto& var : *out_vars) {
VLOG(6) << "Mapping output tensor: " << var.name()
<< " To position: " << out_pos;
(*fwd_outputs_name_pos_map)[var.name()] = out_pos;
out_pos++;
}
}
static void PurifyGradNodeGenerationInfo(const proto::OpProto& op_proto,
GradNodeGenerationInfo* bwd_info) {
auto* op_base_infos = bwd_info->GetMutableOpBaseInfos();
for (auto& iter : *op_base_infos) {
std::map<std::string, std::string>* grad_outs_slotname_map =
iter.GetMutableGradOutsSlotnameMap();
std::map<std::string, std::string>* grad_ins_fwd_slotname_map =
iter.GetMutableGradInsFwdSlotnameMap();
std::map<std::string, std::string>* grad_ins_grad_slotname_map =
iter.GetMutableGradInsGradSlotnameMap();
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>*
grad_ins = iter.GetMutableGradIns();
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>*
grad_outs = iter.GetMutableGradOuts();
// Op Name
const std::string op_name = op_proto.type();
// Handle dispensable inputs
for (const proto::OpProto::Var& input : op_proto.inputs()) {
std::string input_name = input.name();
// Delete dispensable tensor unless specified in op_ins_map
if (input.dispensable()) {
if (!op_ins_map.count(op_name) ||
!op_ins_map[op_name].count(input_name)) {
VLOG(6) << "Removing Dispensable Input: " << input_name;
// grad_outs_slotname_map
auto grad_outs_slotname_map_purified = *grad_outs_slotname_map;
for (const auto& iter : *grad_outs_slotname_map) {
const std::string& grad_output_name = iter.first;
const std::string& matched_input_name = iter.second;
if (matched_input_name == input_name) {
grad_outs_slotname_map_purified.erase(grad_output_name);
PADDLE_ENFORCE(
grad_outs->count(grad_output_name) > 0,
common::errors::Fatal(
"Unable to find gradient output name in grad_outs."));
// grad_outs
grad_outs->erase(grad_output_name);
}
}
*grad_outs_slotname_map = grad_outs_slotname_map_purified;
// grad_ins_fwd_slotname_map: output as tensorwrapper
if (grad_ins_fwd_slotname_map->count(input_name))
grad_ins_fwd_slotname_map->erase(input_name);
// grad_ins: output as tensorwrapper
if (grad_ins->count(input_name)) grad_ins->erase(input_name);
}
}
}
for (const proto::OpProto::Var& output : op_proto.outputs()) {
std::string output_name = output.name();
// Delete dispensable tensor unless specified in op_outs_map
if (output.dispensable()) {
if (!op_outs_map.count(op_name) ||
!op_outs_map[op_name].count(output_name)) {
VLOG(6) << "Removing Dispensable Output: " << output_name;
// grad_ins_grad_slotname_map
auto grad_ins_grad_slotname_map_purified =
*grad_ins_grad_slotname_map;
for (const auto& iter : *grad_ins_grad_slotname_map) {
const std::string& grad_input_name = iter.first;
const std::string& matched_output_name = iter.second;
if (matched_output_name == output_name) {
grad_ins_grad_slotname_map_purified.erase(grad_input_name);
PADDLE_ENFORCE(
grad_ins->count(grad_input_name) > 0,
common::errors::Fatal(
"Unable to find gradient input name in grad_ins."));
// grad_ins
grad_ins->erase(grad_input_name);
}
}
*grad_ins_grad_slotname_map = grad_ins_grad_slotname_map_purified;
// grad_ins_fwd_slotname_map: output as tensorwrapper
if (grad_ins_fwd_slotname_map->count(output_name))
grad_ins_fwd_slotname_map->erase(output_name);
// grad_ins: output as tensorwrapper
if (grad_ins->count(output_name)) grad_ins->erase(output_name);
}
}
}
}
}
/* -------------------------------- */
/* --------- Collect Info --------- */
/* -------------------------------- */
static void CollectForwardInformationFromOpInfo(
const paddle::framework::OpInfo& op_info, ForwardGenerationInfo* fwd_info) {
const proto::OpProto& op_proto = *op_info.proto_;
fwd_info->SetOpType(op_proto.type());
for (const proto::OpProto::Var& input : op_proto.inputs()) {
fwd_info->GetMutableInVars()->push_back(input);
}
for (const proto::OpProto::Var& output : op_proto.outputs()) {
fwd_info->GetMutableOutVars()->push_back(output);
}
}
static bool CollectGradInformationFromOpInfo(
const paddle::framework::OpInfo& op_info,
GradNodeGenerationInfo* bwd_info) {
const proto::OpProto& op_proto = *op_info.proto_;
const std::string& op_type = op_proto.type();
std::vector<int64_t> dims = {1, 1, 1, 1};
/* ------ Prepare "ins" ------ */
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VarBase>>>
ins;
if (op_proto.inputs().size() == 1 && op_proto.outputs().size() == 1 &&
op_proto.inputs()[0].duplicable() &&
!op_proto.outputs()[0].duplicable()) {
VLOG(6) << "Handle op with special op_bases: " << op_type;
// @special case (sum_op): for ops with single duplicable input and single
// non-duplicable output
// feed in NUM_CREATED_DUP_INPUTS inputs to detect a
// special scenario.
const std::string& in_name = op_proto.inputs()[0].name();
ins[in_name] = {};
for (size_t i = 0; i < NUM_CREATED_DUP_INPUTS; i++) {
ins[in_name].emplace_back(std::make_shared<paddle::imperative::VarBase>(
"auto_" + in_name + "_" + std::to_string(i)));
ins[in_name][i]->SetOverriddenStopGradient(false);
ins[in_name][i]->MutableVar()->GetMutable<DenseTensor>();
}
} else {
for (const proto::OpProto::Var& input : op_proto.inputs()) {
const std::string& in_name = input.name();
// Handle dispensable input:
// 1. At python level, dispensable input will be detected at Python-C
// interface and filled with an empty vector
// 2. At C++ level, customers should always pass an empty vector for any
// dispensable input
// 3. During further lowering, there will always be a placeholder VarBase
// in ins/outs no matter whether it's dispensable or not
// As a result, we always create input VarBase regardless of its
// dispensability.
// Handle duplicable input: list(VarBase) or VarBase
// We dont know the exact number of inputs expected,
// but we only need to identify the slot name order,
// therefore fill in 1 single input VarBase is enough in this scenario
ins[in_name] = {
std::make_shared<paddle::imperative::VarBase>("auto_" + in_name)};
ins[in_name][0]->SetOverriddenStopGradient(false);
ins[in_name][0]->MutableVar()->GetMutable<DenseTensor>();
}
}
VLOG(6) << "Prepared Forward Ins Map, size = " << ins.size();
/* ------ Prepare "outs" ------ */
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VarBase>>>
outs;
for (const proto::OpProto::Var& output : op_proto.outputs()) {
const std::string& out_name = output.name();
// We always create output VarBase regardless of its dispensability.
// We dont know the exact number of outputs during code generation,
// however, simply identifying the slot name order would be enough
outs[out_name] = {
std::make_shared<paddle::imperative::VarBase>("auto_" + out_name)};
outs[out_name][0]->SetOverriddenStopGradient(false);
outs[out_name][0]->MutableVar()->GetMutable<DenseTensor>();
}
VLOG(6) << "Prepared Forward Outs Map, size = " << outs.size();
framework::AttributeMap attrs;
paddle::framework::AttributeMap default_attrs;
auto* attr_checker = op_info.Checker();
if (attr_checker) {
VLOG(6) << "Checking AttributeMap Settings";
attr_checker->Check(&attrs, true, /*only_check_exist_value=*/true);
default_attrs = attr_checker->GetDefaultAttrMap();
} else {
VLOG(6) << "Detected Null Attribute Checker, use empty default_attrs";
}
if (operators_with_attrs.count(op_type)) {
VLOG(6) << "Found operator " << op_type << " using special AttributeMap";
attrs = operators_with_attrs[op_type];
}
VLOG(6) << "Prepared Default Attributes Map, size = " << default_attrs.size();
for (const auto& iter : default_attrs) {
VLOG(6) << iter.first;
}
/* ---------------------------- */
/* --------- Backward --------- */
/* ---------------------------- */
/* ------ Fwd paddle::imperative::VariableWrapper Map ------ */
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>
fwd_ins;
std::map<std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>
fwd_outs;
for (const auto& iter : ins) {
fwd_ins[iter.first] = {};
for (const std::shared_ptr<paddle::imperative::VarBase>& var_base :
iter.second) {
fwd_ins[iter.first].push_back(var_base->SharedVar());
}
}
for (const auto& iter : outs) {
fwd_outs[iter.first] = {};
for (const std::shared_ptr<paddle::imperative::VarBase>& var_base :
iter.second) {
fwd_outs[iter.first].push_back(var_base->SharedVar());
}
}
VLOG(6) << "Constructed Forward paddle::imperative::VariableWrapper Map";
/* ------ Run GradOpMaker ------ */
if (!op_info.dygraph_grad_op_maker_) {
VLOG(6) << op_type << " has no GradOpMaker";
bwd_info->SetGenerateForwardOnly(true);
return false;
}
std::shared_ptr<paddle::imperative::GradOpNode> grad_node =
op_info.dygraph_grad_op_maker_(
op_type, ins, outs, attrs, default_attrs, {});
if (!grad_node) {
VLOG(6) << "Got nullptr GradOpNode for " << op_type
<< " likely registered EmptyGradOpMaker";
bwd_info->SetGenerateForwardOnly(true);
return false;
}
VLOG(6) << "Prepared GradOpNode";
/* ---- Collect OpBase's op_types ---- */
bwd_info->SetFwdOpType(op_type);
auto* op_base_infos = bwd_info->GetMutableOpBaseInfos();
op_base_infos->resize(grad_node->size());
for (auto iter = grad_node->begin(); iter < grad_node->end(); iter++) {
// Each OpBase
int index = static_cast<int>(std::distance(grad_node->begin(), iter));
paddle::imperative::OpBase& op_base = *iter;
(*op_base_infos)[index].SetOpBaseType(op_base.Type());
}
/* ------ Get Grad ins/outs/attrs ---- */
VLOG(6) << "In function size: " << grad_node->size();
for (auto iter = grad_node->begin(); iter < grad_node->end(); iter++) {
int index = static_cast<int>(std::distance(grad_node->begin(), iter));
auto* op_base_grad_ins = (*op_base_infos)[index].GetMutableGradIns();
auto* op_base_grad_outs = (*op_base_infos)[index].GetMutableGradOuts();
auto* op_base_grad_attrs = (*op_base_infos)[index].GetMutableGradAttrs();
const paddle::imperative::OpBase& op_base = *iter;
const std::map<std::string, paddle::imperative::SavedVariableWrapperList>&
g_ins = op_base.GetInsMap();
const std::map<std::string, paddle::imperative::SavedVariableWrapperList>&
g_outs = op_base.GetOutsMap();
*op_base_grad_attrs = op_base.Attrs();
for (const auto& it : g_ins) {
if (!op_base_grad_ins->count(it.first))
(*op_base_grad_ins)[it.first] = {};
for (auto vw_iter = it.second.begin(); vw_iter != it.second.end();
vw_iter++) {
std::shared_ptr<paddle::imperative::VariableWrapper> vw = *vw_iter;
(*op_base_grad_ins)[it.first].push_back(vw);
VLOG(6) << "GradIns Name: " << it.first;
}
}
for (const auto& it : g_outs) {
if (!op_base_grad_outs->count(it.first))
(*op_base_grad_outs)[it.first] = {};
for (auto vw_iter = it.second.begin(); vw_iter != it.second.end();
vw_iter++) {
std::shared_ptr<paddle::imperative::VariableWrapper> vw = *vw_iter;
(*op_base_grad_outs)[it.first].push_back(vw);
VLOG(6) << "GradOuts Name: " << it.first;
}
}
auto& inferer = op_base.Info().NoNeedBufferVarsInferer();
if (inferer && !special_no_need_buffer_op_set.count(op_type)) {
*(*op_base_infos)[index].GetMutableNoNeedBufferInputs() =
inferer(g_ins, g_outs, *op_base_grad_attrs);
}
auto& infer_backward_inplace = op_base.Info().infer_inplace_;
if (infer_backward_inplace) {
*(*op_base_infos)[index].GetMutableBackwardInplaceMap() =
infer_backward_inplace(true);
}
}
/* ------ Slot Name Matching ---- */
for (auto& iter : *op_base_infos) {
// grad_ins -> fwd_ins, fwd_outs
SlotNameMatching(iter.GetGradIns(),
fwd_ins,
fwd_outs,
iter.GetMutableGradInsFwdSlotnameMap(),
iter.GetMutableGradInsGradSlotnameMap());
// grad_outs -> fwd_ins, fwd_outs
SlotNameMatching(iter.GetGradOuts(),
fwd_ins,
fwd_outs,
iter.GetMutableGradOutsSlotnameMap(),
iter.GetMutableGradOutsSlotnameMap());
}
VLOG(6) << "Finished Slotname Matching";
return true;
}
/* --------------------------------------------------- */
/* --------- CodeGen: Forward GradNode Creation ------ */
/* --------------------------------------------------- */
static std::string GenerateGradNodeCreationContent(
const ForwardGenerationInfo& fwd_info,
const GradNodeGenerationInfo& bwd_info,
const std::string& trace_op_body_str,
std::map<std::string, std::string> forward_inplace_map = {}) {
VLOG(6) << "Generating GradNode Creation codes";
const std::string& op_type = fwd_info.GetOpType();
const std::unordered_map<std::string, size_t>& fwd_inputs_name_pos_map =
fwd_info.GetFwdInputsNamePosMap();
const std::unordered_map<std::string, size_t>& fwd_outputs_name_pos_map =
fwd_info.GetFwdOutputsNamePosMap();
const std::vector<proto::OpProto::Var>& in_vars = fwd_info.GetInVars();
const std::vector<proto::OpProto::Var>& out_vars = fwd_info.GetOutVars();
const auto& op_base_infos = bwd_info.GetOpBaseInfos();
// [Generation] Construct GradOpNode
// Run ComputeRequiredGrad
// If single output slotname and not duplicable,
// then generate: "egr::AutogradMeta* p_autograd_out =
// egr::EagerUtils::autograd_meta("op_proto->outputs()[0].name()")"
std::string get_input_autograd_meta_str = " // Prepare Autograd Meta\n";
std::string get_output_autograd_meta_str = "";
// If single output slotname and not duplicable,
// then generate: "egr::AutogradMeta* p_autograd_out =
// egr::EagerUtils::autograd_meta("op_proto.outputs()[0].name()")"
for (const proto::OpProto::Var& output : out_vars) {
const std::string& output_name = output.name();
const std::string& output_autograd_name =
"p_autograd_" + LegalizeVarName(output_name);
// output autograd_meta should be got after running TraceOP.
if (output.duplicable()) {
const char* GET_MULTI_AUTOGRAD_META_TEMPLATE =
" std::vector<egr::AutogradMeta*> %s = "
"egr::EagerUtils::autograd_meta(&%s);\n";
get_output_autograd_meta_str +=
paddle::string::Sprintf(GET_MULTI_AUTOGRAD_META_TEMPLATE,
output_autograd_name,
LegalizeVarName(output_name));
} else {
// In inplace op, the case where output is duplicable is not considered.
// Replace output directly with input in inplace op.
if (!forward_inplace_map.empty() &&
forward_inplace_map.count(output_name)) {
auto inplace_input_name =
LegalizeVarName(forward_inplace_map[output_name]);
const std::string& inplace_input_autograd_name =
"p_autograd_" + inplace_input_name;
const char* GET_SINGLE_AUTOGRAD_META_TEMPLATE =
" %s = egr::EagerUtils::autograd_meta(&%s);\n";
get_output_autograd_meta_str +=
paddle::string::Sprintf(GET_SINGLE_AUTOGRAD_META_TEMPLATE,
inplace_input_autograd_name,
inplace_input_name);
} else {
const char* GET_SINGLE_AUTOGRAD_META_TEMPLATE =
" egr::AutogradMeta* %s = "
"egr::EagerUtils::autograd_meta(&%s);\n";
get_output_autograd_meta_str +=
paddle::string::Sprintf(GET_SINGLE_AUTOGRAD_META_TEMPLATE,
output_autograd_name,
LegalizeVarName(output_name));
}
}
}
VLOG(6) << "Generated outputs autograd_meta";
// input autograd_meta should be got before running TraceOP (for checking
// inplace).
for (const proto::OpProto::Var& input : in_vars) {
const std::string& input_name = input.name();
const std::string& input_autograd_name =
"p_autograd_" + LegalizeVarName(input_name);
if (input.duplicable()) {
const char* GET_MULTI_AUTOGRAD_META_TEMPLATE =
" std::vector<egr::AutogradMeta*> %s = "
"egr::EagerUtils::nullable_autograd_meta(%s);\n";
get_input_autograd_meta_str +=
paddle::string::Sprintf(GET_MULTI_AUTOGRAD_META_TEMPLATE,
input_autograd_name,
LegalizeVarName(input_name));
} else if (input.dispensable()) {
const char* GET_SINGLE_AUTOGRAD_META_TEMPLATE =
" egr::AutogradMeta* %s = "
"egr::EagerUtils::nullable_autograd_meta(%s);\n";
get_input_autograd_meta_str +=
paddle::string::Sprintf(GET_SINGLE_AUTOGRAD_META_TEMPLATE,
input_autograd_name,
LegalizeVarName(input_name));
} else {
const char* GET_SINGLE_AUTOGRAD_META_TEMPLATE =
" egr::AutogradMeta* %s = "
"egr::EagerUtils::nullable_autograd_meta(%s);\n";
get_input_autograd_meta_str +=
paddle::string::Sprintf(GET_SINGLE_AUTOGRAD_META_TEMPLATE,
input_autograd_name,
LegalizeVarName(input_name));
}
}
VLOG(6) << "Generated inputs autograd_meta";
// check inplace input to avoid inplace operations on leaf nodes with
// stop_gradient=False.
std::string check_inplace_str = "";
if (!forward_inplace_map.empty()) {
const char* CHECKING_INPLACE_TEMPLATE =
" // Check Inplace\n"
" egr::EagerUtils::CheckInplace(%s, p_autograd_%s, "
"require_any_grad);\n";
for (auto& inplace_pair : forward_inplace_map) {
std::string inplace_name = LegalizeVarName(inplace_pair.second);
check_inplace_str += paddle::string::Sprintf(
CHECKING_INPLACE_TEMPLATE, inplace_name, inplace_name);
}
VLOG(6) << "Check Inplace Input";
}
std::string prepare_autograd_meta_str = "";
// only generate input autograd_meta in temporary.
// output autograd_meta will be generated after running TraceOP.
prepare_autograd_meta_str += get_input_autograd_meta_str;
prepare_autograd_meta_str += "\n";
// [GradOpNode] GetTraceBackward
std::string trace_backward_str =
" bool trace_backward = egr::Controller::Instance().HasGrad();\n";
prepare_autograd_meta_str += trace_backward_str;
prepare_autograd_meta_str += "\n";
// [GradOpNode] Generation
std::string grad_node_creation_str = "";
size_t bwd_in_slot_num = out_vars.size();
size_t bwd_out_slot_num = in_vars.size();
const char* GRAD_OP_NODE_TEMPLATE =
" auto grad_node = std::shared_ptr<%sGradNodeCompat>(new "
"%sGradNodeCompat(%d, "
"%d)); // NOLINT\n";
grad_node_creation_str += " // Create GradOpNode\n";
grad_node_creation_str += paddle::string::Sprintf(GRAD_OP_NODE_TEMPLATE,
op_type,
op_type,
bwd_in_slot_num,
bwd_out_slot_num);
grad_node_creation_str += "\n";
VLOG(6) << "Generated GradOpNode construction";
// [GradOpNode] Set Attrs
grad_node_creation_str += " // Set Attributes\n";
grad_node_creation_str += " grad_node->SetAttrMap(std::move(attrs));\n";
grad_node_creation_str +=
" grad_node->SetDefaultAttrMap(std::move(default_attrs));\n";
grad_node_creation_str += "\n";
// [GradOpNode] Set TensorWrappers
grad_node_creation_str += " // Set Tensor Wrappers\n";
for (const auto& iter : op_base_infos) {
const std::map<std::string, std::string>& grad_ins_fwd_slotname_map =
iter.GetGradInsFwdSlotnameMap();
for (auto& kv : grad_ins_fwd_slotname_map) {
const std::string& tensor_wrapper_name = kv.second;
const char* SET_TENSOR_WRAPPER_TEMPLATE =
" grad_node->SetTensorWrapper_%s(%s);\n";
// Replace output directly with input in inplace op.
if (!forward_inplace_map.empty() &&
forward_inplace_map.count(tensor_wrapper_name)) {
auto inplace_input_name = forward_inplace_map[tensor_wrapper_name];
grad_node_creation_str +=
paddle::string::Sprintf(SET_TENSOR_WRAPPER_TEMPLATE,
LegalizeVarName(tensor_wrapper_name),
LegalizeVarName(inplace_input_name));
} else {
grad_node_creation_str +=
paddle::string::Sprintf(SET_TENSOR_WRAPPER_TEMPLATE,
LegalizeVarName(tensor_wrapper_name),
LegalizeVarName(tensor_wrapper_name));
}
}
}
grad_node_creation_str += "\n";
VLOG(6) << "Generated SetTensorWrapper";
// [GradOpNode] SetGradOutMeta
// [GradOpNode] Add Edges
std::string compute_require_grad_args = "trace_backward";
for (const proto::OpProto::Var& input : in_vars) {
const std::string& input_name = input.name();
const std::string& input_autograd_name =
"p_autograd_" + LegalizeVarName(input_name);
if (!input.duplicable()) {
compute_require_grad_args += ", " + input_autograd_name;
size_t input_position = fwd_inputs_name_pos_map.at(input_name);
bool found_target_name = false;
for (const auto& iter : op_base_infos) {
const auto& grad_outs_slot_map = iter.GetGradOutsSlotnameMap();
for (auto const& iter : grad_outs_slot_map) {
if ((!found_target_name) && (input_name == iter.second)) {
const char* SET_GRAD_OUT_META_TEMPLATE =
" grad_node->SetGradOutMeta(%s, %d);\n";
grad_node_creation_str +=
paddle::string::Sprintf(SET_GRAD_OUT_META_TEMPLATE,
LegalizeVarName(input_name),
input_position);
found_target_name = true;
}
}
}
} else {
compute_require_grad_args += ", &" + input_autograd_name;
size_t input_position = fwd_inputs_name_pos_map.at(input_name);
bool found_target_name = false;
for (const auto& iter : op_base_infos) {
const auto& grad_outs_slot_map = iter.GetGradOutsSlotnameMap();
for (auto const& iter : grad_outs_slot_map) {
if ((!found_target_name) && (input_name == iter.second)) {
const char* SET_GRAD_OUT_META_TEMPLATE =
" grad_node->SetGradOutMeta(%s, %d);\n";
grad_node_creation_str +=
paddle::string::Sprintf(SET_GRAD_OUT_META_TEMPLATE,
LegalizeVarName(input_name),
input_position);
found_target_name = true;
}
}
}
}
}
// [GradOpNode] SetGradInMeta
// [AutogradMeta] SetOutRank
// [AutogradMeta] SetHistory
std::string pass_stop_gradient_args = "false";
for (const proto::OpProto::Var& output : out_vars) {
const std::string& output_name = output.name();
// Replace output directly with input in inplace op.
if (!forward_inplace_map.empty() &&
forward_inplace_map.count(output_name)) {
auto inplace_input_name = forward_inplace_map[output_name];
const std::string& inplace_input_autograd_name =
"p_autograd_" + LegalizeVarName(inplace_input_name);
size_t output_position = fwd_outputs_name_pos_map.at(output_name);
// Intermediate Tensor does not require SetHistory, nor RetainGrad
pass_stop_gradient_args += ", " + inplace_input_autograd_name;
const char* SET_OUT_RANK_TEMPLATE =
" egr::EagerUtils::SetOutRankWithSlot(%s, %d);\n";
grad_node_creation_str += paddle::string::Sprintf(
SET_OUT_RANK_TEMPLATE, inplace_input_autograd_name, output_position);
// Intermediate Tensor does not require SetHistory
if (!output.intermediate()) {
const char* SET_HISTORY_TEMPLATE =
" egr::EagerUtils::SetHistory(%s, grad_node);\n";
grad_node_creation_str += paddle::string::Sprintf(
SET_HISTORY_TEMPLATE, inplace_input_autograd_name);
}
const char* SET_GRAD_IN_META_TEMPLATE =
" grad_node->SetGradInMeta(%s, %d);\n";
grad_node_creation_str +=
paddle::string::Sprintf(SET_GRAD_IN_META_TEMPLATE,
LegalizeVarName(inplace_input_name),
output_position);
} else {
const std::string& output_autograd_name =
"p_autograd_" + LegalizeVarName(output_name);
size_t output_position = fwd_outputs_name_pos_map.at(output_name);
// Intermediate Tensor does not require SetHistory, nor RetainGrad
if (output.duplicable()) {
pass_stop_gradient_args += ", &" + output_autograd_name;
const char* SET_OUT_RANK_TEMPLATE =
" egr::EagerUtils::SetOutRankWithSlot(&%s, %d);\n";
grad_node_creation_str += paddle::string::Sprintf(
SET_OUT_RANK_TEMPLATE, output_autograd_name, output_position);
// Intermediate Tensor does not require SetHistory
if (!output.intermediate()) {
const char* SET_HISTORY_TEMPLATE =
" egr::EagerUtils::SetHistory(&%s, grad_node);\n";
grad_node_creation_str += paddle::string::Sprintf(
SET_HISTORY_TEMPLATE, output_autograd_name);
}
const char* SET_GRAD_IN_META_TEMPLATE =
" grad_node->SetGradInMeta(%s, %d);\n";
grad_node_creation_str +=
paddle::string::Sprintf(SET_GRAD_IN_META_TEMPLATE,
LegalizeVarName(output_name),
output_position);
} else {
pass_stop_gradient_args += ", " + output_autograd_name;
const char* SET_OUT_RANK_TEMPLATE =
" egr::EagerUtils::SetOutRankWithSlot(%s, %d);\n";
grad_node_creation_str += paddle::string::Sprintf(
SET_OUT_RANK_TEMPLATE, output_autograd_name, output_position);
// Intermediate Tensor does not require SetHistory
if (!output.intermediate()) {
const char* SET_HISTORY_TEMPLATE =
" egr::EagerUtils::SetHistory(%s, grad_node);\n";
grad_node_creation_str += paddle::string::Sprintf(
SET_HISTORY_TEMPLATE, output_autograd_name);
}
const char* SET_GRAD_IN_META_TEMPLATE =
" grad_node->SetGradInMeta(%s, %d);\n";
grad_node_creation_str +=
paddle::string::Sprintf(SET_GRAD_IN_META_TEMPLATE,
LegalizeVarName(output_name),
output_position);
}
}
}
VLOG(6) << "Generated SetGradIn/OutMeta";
// [Generation] GradNode Creation
// After getting require_any_grad, firstly use CheckInplace method for inplace
// op.
// Then execute TraceOp and generate output autograd_meta.
// Finally, Construct GradNode. (Replace output directly with input in inplace
// op.)
// Add event record
std::string event_name = op_type + " node_creation";
const char* GRAD_NODE_CREATION_TEMPLATE =
"%s"
" bool require_any_grad = egr::EagerUtils::ComputeRequireGrad(%s);\n"
"%s\n"
"%s"
" {\n"
" phi::RecordEvent node_creation_record_event(\"%s\", "
"phi::TracerEventType::OperatorInner, 1);\n"
"%s"
" if(require_any_grad) {\n"
" VLOG(6) << \" Construct Grad for %s \";\n"
" egr::EagerUtils::PassStopGradient(%s);\n"
" %s\n"
" }\n"
" }";
std::string grad_node_creation_body_str =
paddle::string::Sprintf(GRAD_NODE_CREATION_TEMPLATE,
prepare_autograd_meta_str,
compute_require_grad_args,
check_inplace_str,
trace_op_body_str,
event_name,
get_output_autograd_meta_str,
op_type,
pass_stop_gradient_args,
grad_node_creation_str);
return grad_node_creation_body_str;
}
/* -------------------------------- */
/* --------- CodeGen: Forward ----- */
/* -------------------------------- */
static std::pair<std::string, std::string> GenerateForwardFunctionContents(
const ForwardGenerationInfo& fwd_info,
const GradNodeGenerationInfo& bwd_info,
std::map<std::string, std::string> forward_inplace_map = {}) {
/* --- Process Forward Info ---*/
const std::string& op_type = fwd_info.GetOpType();
const std::unordered_map<std::string, size_t>& fwd_inputs_name_pos_map =
fwd_info.GetFwdInputsNamePosMap();
const std::unordered_map<std::string, size_t>& fwd_outputs_name_pos_map =
fwd_info.GetFwdOutputsNamePosMap();
const std::vector<proto::OpProto::Var>& in_vars = fwd_info.GetInVars();
const std::vector<proto::OpProto::Var>& out_vars = fwd_info.GetOutVars();
/*
// Forward Function Example:
std::tuple<vector<Tensor>, Tensor, vector<Tensor>>
kernel_function(vector<Tensor>& X, Tensor& Y, const paddle::AttributeMap&
attr_map, size_t
Out0Num, size_t Out1Num) {
// Forward Function Body
// According to fwd_inputs_name_pos_map
std::map<std::string, std::vector<std::shared_ptr<egr::EagerVariable>>>
ins =
{ {"X" , TrySyncToVars(X)}, { "Y" , TrySyncToVars(Y)} };
std::map<std::string, std::vector<std::shared_ptr<egr::EagerVariable>>>
outs =
{
{"Out0" , CreateVars(Out0Num)}, {"Out1"
,CreateVars(Out1Num)} };
// According to op_proto->attrs()
Controller.Instance().GetCurrentTracer()->TraceOp("op_type", ins, outs,
attr_map,
Controller.Instance().GetExpectedPlace(), {});
// According to fwd_outputs_names
std::vector<paddle::Tensor> Out0 =
GetOutputs(outs["Out0"]);
paddle::Tensor Out1 = GetOutputs(outs["Out1"][0]);
std::vector<paddle::Tensor> Out2 =
GetOutputs(outs["Out2"]);
// Grad Node Generation Codes
...
return std::make_tuple(Out0, Out1, Out2);
}
*/
VLOG(6) << "Generating Dygraph Forward Function";
const char* FORWARD_FUNCTION_TEMPLATE =
" VLOG(3) << \"Running Eager Forward Op: %s\";\n";
std::string generated_function_body =
paddle::string::Sprintf(FORWARD_FUNCTION_TEMPLATE, op_type);
std::string dygraph_function_args_str = "";
std::string amp_function_call_args_str = "";
core_ops_legacy_args_info[op_type] = {};
core_ops_legacy_args_type_info[op_type] = {};
core_ops_legacy_args_info[op_type].resize(in_vars.size());
core_ops_legacy_args_type_info[op_type].resize(in_vars.size());
/* ------ Dygraph forward function generation ------ */
generated_function_body += " // Dygraph Forward Pass\n";
generated_function_body += "\n";
// [Generation] Get Ins Map
std::string ins_contents_str = "";
std::vector<std::string> input_args_str_list(in_vars.size());
std::vector<std::string> amp_function_call_args_str_list(in_vars.size());
std::string amp_tensors_vector_str = "";
std::string amp_auto_cast_str = "";
for (const proto::OpProto::Var& input : in_vars) {
const std::string& input_name = input.name();
size_t input_position = fwd_inputs_name_pos_map.at(input_name);
if (input.duplicable()) {
const char* FWD_INS_ARG_TEMPLATE =
"const std::vector<paddle::Tensor>& %s";
input_args_str_list[input_position] = paddle::string::Sprintf(
FWD_INS_ARG_TEMPLATE, LegalizeVarName(input_name));
amp_function_call_args_str_list[input_position] =
" NEW_" + LegalizeVarName(input_name);
core_ops_legacy_args_type_info[op_type][input_position] = "list";
} else {
// inplace tensor can't be const
const char* FWD_INS_ARG_TEMPLATE;
bool flag_find_input_name = false;
if (!forward_inplace_map.empty()) {
for (auto& inplace_pair : forward_inplace_map) {
if (inplace_pair.second == input_name) {
flag_find_input_name = true;
FWD_INS_ARG_TEMPLATE = "paddle::Tensor& %s";
break;
}
}
}
if (!flag_find_input_name) {
FWD_INS_ARG_TEMPLATE = "const paddle::Tensor& %s";
}
input_args_str_list[input_position] = paddle::string::Sprintf(
FWD_INS_ARG_TEMPLATE, LegalizeVarName(input_name));
amp_function_call_args_str_list[input_position] =
" NEW_" + LegalizeVarName(input_name);
core_ops_legacy_args_type_info[op_type][input_position] = "tensor";
}
core_ops_legacy_args_info[op_type][input_position] = input_name;
if (input.dispensable()) continue;
const char* FWD_INS_CONTENT_TEMPLATE =
"{ \"%s\", egr::EagerUtils::TrySyncToVars(%s) },";
ins_contents_str += paddle::string::Sprintf(
FWD_INS_CONTENT_TEMPLATE, input_name, LegalizeVarName(input_name));
if (input.duplicable()) {
const char* AMP_TENSORS_VECTOR_TEMPLATE = "%s,";
amp_tensors_vector_str +=
paddle::string::Sprintf(AMP_TENSORS_VECTOR_TEMPLATE, input_name);
const char* AMP_AUTO_CAST_TEMPLATE =
" auto NEW_%s = egr::AmpAutoCasts(\"%s\", %s, amp_dst_dtype, "
"\"%s\");\n";
amp_auto_cast_str += paddle::string::Sprintf(AMP_AUTO_CAST_TEMPLATE,
LegalizeVarName(input_name),
input_name,
LegalizeVarName(input_name),
op_type);
} else {
const char* AMP_TENSORS_VECTOR_TEMPLATE = "{%s},";
amp_tensors_vector_str += paddle::string::Sprintf(
AMP_TENSORS_VECTOR_TEMPLATE, LegalizeVarName(input_name));
const char* AMP_AUTO_CAST_TEMPLATE =
" auto NEW_%s = egr::AmpAutoCast(\"%s\", %s, amp_dst_dtype, "
"\"%s\");\n";
amp_auto_cast_str += paddle::string::Sprintf(AMP_AUTO_CAST_TEMPLATE,
LegalizeVarName(input_name),
input_name,
LegalizeVarName(input_name),
op_type);
}
}
if (!ins_contents_str.empty())
ins_contents_str.pop_back(); // // Remove trailing ","
if (!amp_tensors_vector_str.empty()) amp_tensors_vector_str.pop_back();
for (const std::string& arg : input_args_str_list) {
dygraph_function_args_str += arg;
dygraph_function_args_str += ",";
}
if (!dygraph_function_args_str.empty()) dygraph_function_args_str.pop_back();
for (const std::string& arg : amp_function_call_args_str_list) {
amp_function_call_args_str += arg;
amp_function_call_args_str += ",";
}
if (!amp_function_call_args_str.empty())
amp_function_call_args_str.pop_back();
// Handle Dispensable Inputs
std::string dispensable_ins_contents_str = "";
std::string dispensable_amp_tensors_vector_str = "";
std::string dispensable_amp_auto_cast_str = "";
std::set<std::string> input_names;
for (const proto::OpProto::Var& input : in_vars) {
const std::string& input_name = input.name();
input_names.insert(input_name);
if (input.dispensable()) {
if (input.duplicable()) {
const char* FWD_INS_CONTENT_TEMPLATE =
" if(%s.size() > 0) "
"ins[\"%s\"] = egr::EagerUtils::TrySyncToVars(%s);\n";
dispensable_ins_contents_str +=
paddle::string::Sprintf(FWD_INS_CONTENT_TEMPLATE,
LegalizeVarName(input_name),
input_name,
LegalizeVarName(input_name));
const char* FWD_AMP_TENSORS_VECTOR_TEMPLATE =
" if(%s.size() > 0) "
"amp_tensors_vector.push_back(%s);\n";
dispensable_amp_tensors_vector_str +=
paddle::string::Sprintf(FWD_AMP_TENSORS_VECTOR_TEMPLATE,
LegalizeVarName(input_name),
LegalizeVarName(input_name));
const char* DISPENSABLE_AMP_AUTO_CAST_TEMPLATE =
" auto NEW_%s = ((%s.size() > 0) ? egr::AmpAutoCasts(\"%s\", "
"%s, amp_dst_dtype, \"%s\") : %s);\n";
dispensable_amp_auto_cast_str +=
paddle::string::Sprintf(DISPENSABLE_AMP_AUTO_CAST_TEMPLATE,
LegalizeVarName(input_name),
LegalizeVarName(input_name),
input_name,
LegalizeVarName(input_name),
op_type,
LegalizeVarName(input_name));
} else {
const char* FWD_INS_CONTENT_TEMPLATE =
" if(%s.has_allocation()) "
"ins[\"%s\"] = egr::EagerUtils::TrySyncToVars(%s);\n";
dispensable_ins_contents_str +=
paddle::string::Sprintf(FWD_INS_CONTENT_TEMPLATE,
LegalizeVarName(input_name),
input_name,
LegalizeVarName(input_name));
const char* FWD_AMP_TENSORS_VECTOR_TEMPLATE =
" if(%s.has_allocation()) "
"amp_tensors_vector.push_back({ %s });\n";
dispensable_amp_tensors_vector_str +=
paddle::string::Sprintf(FWD_AMP_TENSORS_VECTOR_TEMPLATE,
LegalizeVarName(input_name),
LegalizeVarName(input_name));
const char* DISPENSABLE_AMP_AUTO_CAST_TEMPLATE =
" auto NEW_%s = ((%s.has_allocation()) ? "
"egr::AmpAutoCast(\"%s\", "
"%s, amp_dst_dtype, \"%s\") : %s);\n";
dispensable_amp_auto_cast_str +=
paddle::string::Sprintf(DISPENSABLE_AMP_AUTO_CAST_TEMPLATE,
LegalizeVarName(input_name),
LegalizeVarName(input_name),
input_name,
LegalizeVarName(input_name),
op_type,
LegalizeVarName(input_name));
}
}
}
VLOG(6) << "Generated Ins Map";
// [Generation] Get Outs Map
std::string outs_contents_str = "";
std::string inplace_mapping_str = "";
for (const proto::OpProto::Var& output : out_vars) {
const std::string& output_name = output.name();
std::string outnum = "1";
if (op_passing_outs_map[op_type].count(output_name)) {
const std::string output_var_name = output_name + "Var";
// Pass Output from function
// argument(EagerVariable*/vector<EagerVariable*>&),
// in form of shared_ptr<EagerVariable>/vector<shared_ptr<EagerVariable>>
if (output.duplicable()) {
const char* FWD_NUM_ARG_TEMPLATE = ", std::vector<paddle::Tensor*>& %s";
std::string arg_str = paddle::string::Sprintf(
FWD_NUM_ARG_TEMPLATE, LegalizeVarName(output_var_name));
dygraph_function_args_str += arg_str;
amp_function_call_args_str += (", " + LegalizeVarName(output_var_name));
core_ops_legacy_args_type_info[op_type].push_back("list");
} else {
const char* FWD_NUM_ARG_TEMPLATE = ", paddle::Tensor* %s";
std::string arg_str = paddle::string::Sprintf(
FWD_NUM_ARG_TEMPLATE, LegalizeVarName(output_var_name));
dygraph_function_args_str += arg_str;
amp_function_call_args_str += (", " + LegalizeVarName(output_var_name));
core_ops_legacy_args_type_info[op_type].push_back("tensor");
}
if (BeSameAsInput(output_name, input_names)) {
if (!output.dispensable()) {
std::string input_name =
output_name.substr(0, output_name.size() - 3);
const char* FWD_OUTS_CONTENT_TEMPLATE = R"({ "%s", ins["%s"] },)";
outs_contents_str += paddle::string::Sprintf(
FWD_OUTS_CONTENT_TEMPLATE, output_name, input_name);
}
} else {
const char* FWD_OUTS_CONTENT_TEMPLATE =
"{ \"%s\", egr::EagerUtils::TrySyncToVars(%s) },";
outs_contents_str +=
paddle::string::Sprintf(FWD_OUTS_CONTENT_TEMPLATE,
output_name,
LegalizeVarName(output_var_name));
}
core_ops_legacy_args_info[op_type].push_back(output_name);
} else if (!forward_inplace_map.empty() &&
forward_inplace_map.count(output_name)) {
// In inplace op, replace the output with the input directly.
PADDLE_ENFORCE_NE(
forward_inplace_map[output_name],
"",
common::errors::InvalidArgument(
"Inplace op %s has no input corresponding to output %s.",
op_type,
output_name));
const char* FWD_OUTS_CONTENT_TEMPLATE = R"({ "%s", ins["%s"] },)";
auto inplace_input_name = forward_inplace_map[output_name];
outs_contents_str += paddle::string::Sprintf(
FWD_OUTS_CONTENT_TEMPLATE, output_name, inplace_input_name);
// inplace_map used in TraceOp.
const char* INPLACE_MAPPING_TEMPLATE = R"({"%s", "%s"},)";
inplace_mapping_str += paddle::string::Sprintf(
INPLACE_MAPPING_TEMPLATE, inplace_input_name, output_name);
} else {
if (output.duplicable()) {
outnum = output_name + "Num";
const char* FWD_NUM_ARG_TEMPLATE = ", size_t %s";
std::string arg_str =
paddle::string::Sprintf(FWD_NUM_ARG_TEMPLATE, outnum);
dygraph_function_args_str += arg_str;
amp_function_call_args_str += (", " + outnum);
const char* FWD_OUTS_CONTENT_TEMPLATE =
"{ \"%s\", egr::EagerUtils::CreateVars(%s) },";
outs_contents_str += paddle::string::Sprintf(
FWD_OUTS_CONTENT_TEMPLATE, output_name, outnum);
core_ops_legacy_args_info[op_type].push_back(outnum);
core_ops_legacy_args_type_info[op_type].push_back("int");
} else {
const char* FWD_OUTS_CONTENT_TEMPLATE =
"{ \"%s\", "
"{std::make_shared<egr::EagerVariable>(egr::Controller::Instance()."
"GenerateUniqueName())}},";
outs_contents_str +=
paddle::string::Sprintf(FWD_OUTS_CONTENT_TEMPLATE, output_name);
}
}
}
if (!outs_contents_str.empty())
outs_contents_str.pop_back(); // Remove trailing ","
if (!inplace_mapping_str.empty())
inplace_mapping_str.pop_back(); // Remove trailing ","
if ((op_type != "cast") && (forward_inplace_map.empty())) {
VLOG(6) << "Generating Dygraph Forward AMP";
const char* AMP_LOGIC_CONTEXT =
" if (egr::Controller::Instance().GetAMPLevel() != "
"paddle::imperative::AmpLevel::O0) {\n"
" VLOG(5) << \"Check and Prepare For AMP\";\n"
" \n"
"%s\n"
" }\n";
std::string amp_logic_str = "";
if (!in_vars.empty()) {
const char* AMP_TENSORS_VECTOR_TEMPLATE =
" paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize> "
"amp_tensors_vector = { "
"%s };\n";
std::string amp_tensors_vector = paddle::string::Sprintf(
AMP_TENSORS_VECTOR_TEMPLATE, amp_tensors_vector_str);
amp_tensors_vector += dispensable_amp_tensors_vector_str;
amp_logic_str += amp_tensors_vector;
amp_logic_str += "\n";
const char* GET_AMP_GET_DST_DTYPE_CONTEXT =
" auto amp_dst_dtype = "
"paddle::imperative::GetAmpDestDtype(\"%s\", "
"amp_tensors_vector);\n";
amp_logic_str +=
paddle::string::Sprintf(GET_AMP_GET_DST_DTYPE_CONTEXT, op_type);
amp_logic_str += "\n";
amp_logic_str += amp_auto_cast_str;
amp_logic_str += dispensable_amp_auto_cast_str;
amp_logic_str += "\n";
}
const char* CALL_BACK_TEMPLATE =
" {\n"
" paddle::imperative::AutoCastGuard "
"guard(egr::Controller::Instance().GetCurrentAmpAttrs(), "
"paddle::imperative::AmpLevel::O0);\n"
" return %s_dygraph_function(%s);\n"
" }";
amp_function_call_args_str += ", attr_map ";
if (!amp_function_call_args_str.empty()) {
auto iter = amp_function_call_args_str.begin();
if ((*iter) == ',') amp_function_call_args_str.erase(iter);
}
std::string call_back_str = paddle::string::Sprintf(
CALL_BACK_TEMPLATE, op_type, amp_function_call_args_str);
amp_logic_str += call_back_str;
amp_logic_str += "\n";
std::string amp_context =
paddle::string::Sprintf(AMP_LOGIC_CONTEXT, amp_logic_str);
generated_function_body += amp_context;
generated_function_body += "\n";
}
if (!forward_inplace_map.empty()) {
generated_function_body +=
" auto current_level = egr::Controller::Instance().GetAMPLevel();\n";
generated_function_body +=
" "
"egr::Controller::Instance().SetAMPLevel(paddle::imperative::AmpLevel::"
"O0);\n";
}
// forward ins insert
const char* FWD_INS_MAP_TEMPLATE =
" std::map<std::string, "
"std::vector<std::shared_ptr<egr::EagerVariable>>> ins = { "
"%s };\n";
std::string ins_map_str =
paddle::string::Sprintf(FWD_INS_MAP_TEMPLATE, ins_contents_str);
ins_map_str += dispensable_ins_contents_str;
generated_function_body += ins_map_str;
generated_function_body += "\n";
// forward outs insert
const char* FWD_OUTS_MAP_TEMPLATE =
" std::map<std::string, "
"std::vector<std::shared_ptr<egr::EagerVariable>>> outs = { "
"%s };\n";
std::string outs_map_str =
paddle::string::Sprintf(FWD_OUTS_MAP_TEMPLATE, outs_contents_str);
generated_function_body += outs_map_str;
generated_function_body += "\n";
for (const proto::OpProto::Var& output : out_vars) {
const std::string& output_name = output.name();
if (op_passing_outs_map[op_type].count(output_name)) {
if (BeSameAsInput(output_name, input_names)) {
if (output.dispensable()) {
std::string input_name =
output_name.substr(0, output_name.size() - 3);
const char* FWD_OUTS_CONTENT_TEMPLATE =
" if (ins.count(\"%s\")) outs[\"%s\"] = ins[\"%s\"];\n";
generated_function_body += paddle::string::Sprintf(
FWD_OUTS_CONTENT_TEMPLATE, input_name, output_name, input_name);
}
}
}
}
VLOG(6) << "Generated Outs Map";
// [Generation] Apply View Strategy (Tensor)
if (forward_inplace_map.empty() && view_op_map.count(op_type)) {
const char* HANDLE_VIEW_BETWEEN_INPUT_AND_OUTPUT =
" if (ins.count(\"%s\") && outs.count(\"%s\")) {\n"
" egr::EagerUtils::HandleViewBetweenInputAndOutput(ins[\"%s\"][0], "
"outs[\"%s\"][0]);\n"
" };\n";
std::string view_strategy_str = "";
std::string view_input_name = view_op_map[op_type].first;
std::string view_output_name = view_op_map[op_type].second;
view_strategy_str +=
paddle::string::Sprintf(HANDLE_VIEW_BETWEEN_INPUT_AND_OUTPUT,
view_input_name,
view_output_name,
view_input_name,
view_output_name);
generated_function_body += view_strategy_str;
generated_function_body += "\n";
VLOG(6) << "Generated View Strategy";
}
generated_function_body += "\n";
// [Generation] Get Attrs
dygraph_function_args_str +=
", const paddle::framework::AttributeMap& attr_map";
/* --------- Generate TraceOp ----- */
// TraceOp should be run after compute require_any_grad. (for checking
// inplace)
// `trace_op_body_str` will be passed as a parameter to
// `GenerateGradNodeCreationContent`.
std::string trace_op_body_str = "";
// [Generation] Get TraceOp
const char* FWD_TRACE_OP_TEMPLATE =
" paddle::framework::AttributeMap attrs = attr_map;\n"
" paddle::framework::AttributeMap default_attrs;\n"
" egr::Controller::Instance().GetCurrentTracer()->TraceOp(\"%s\", ins, "
"outs, attrs,\n"
" egr::Controller::Instance().GetExpectedPlace(),\n"
" &default_attrs, true, {%s});\n";
std::string trace_op_str = paddle::string::Sprintf(
FWD_TRACE_OP_TEMPLATE, op_type, inplace_mapping_str);
trace_op_body_str += trace_op_str;
trace_op_body_str += "\n";
// [Generation] Log memory information
const char* LOG_MEMORY_INFO_TEMPLATE =
" // Log memory information\n"
" "
"paddle::memory::LogDeviceMemoryStats(egr::Controller::Instance()."
"GetExpectedPlace(), \"%s\");\n";
std::string log_memory_info_str =
paddle::string::Sprintf(LOG_MEMORY_INFO_TEMPLATE, op_type);
trace_op_body_str += log_memory_info_str;
trace_op_body_str += "\n";
VLOG(6) << "Generated AttrMap & TraceOp";
// [Generation] Convert output VarBase to Vector/Tensor
size_t output_size = out_vars.size();
std::vector<std::string> return_contents(output_size);
std::vector<std::string> return_types(output_size);
for (const proto::OpProto::Var& output : out_vars) {
const std::string& output_name = output.name();
const std::string output_var_args_name =
LegalizeVariableName(output_name + "Var");
std::string out_tensor_str;
size_t return_position = fwd_outputs_name_pos_map.at(output_name);
std::string output_varname = LegalizeVariableName(output_name);
if (output.duplicable()) {
if (op_passing_outs_map[op_type].count(output_name)) {
if (output.dispensable()) {
const char* FWD_OUT_TENSORS_TEMPLATE =
" std::vector<paddle::Tensor> %s;\n"
" if (outs.count(\"%s\")) "
"egr::EagerUtils::GetOutputs(outs[\"%s\"], %s);\n"
" egr::EagerUtils::Output2Result(%s, &%s);\n";
out_tensor_str = paddle::string::Sprintf(FWD_OUT_TENSORS_TEMPLATE,
output_varname,
output_name,
output_name,
output_var_args_name,
output_var_args_name,
output_varname);
} else {
const char* FWD_OUT_TENSORS_TEMPLATE =
" std::vector<paddle::Tensor> %s;\n"
" egr::EagerUtils::GetOutputs(outs[\"%s\"], %s);\n"
" egr::EagerUtils::Output2Result(%s, &%s);\n";
out_tensor_str = paddle::string::Sprintf(FWD_OUT_TENSORS_TEMPLATE,
output_varname,
output_name,
output_var_args_name,
output_var_args_name,
output_varname);
}
} else {
const char* FWD_OUT_TENSORS_TEMPLATE =
" std::vector<paddle::Tensor> %s;\n"
" egr::EagerUtils::GetOutputs(outs[\"%s\"], &%s);\n";
out_tensor_str = paddle::string::Sprintf(FWD_OUT_TENSORS_TEMPLATE,
output_varname,
output_name,
output_varname);
}
return_types[return_position] = "std::vector<paddle::Tensor>";
} else {
if (op_passing_outs_map[op_type].count(output_name)) {
if (output.dispensable()) {
const char* FWD_OUT_TENSOR_TEMPLATE =
" if (outs.count(\"%s\")) "
"egr::EagerUtils::GetOutput(outs[\"%s\"][0], %s);\n"
" paddle::Tensor& %s = *%s;\n";
out_tensor_str = paddle::string::Sprintf(FWD_OUT_TENSOR_TEMPLATE,
output_name,
output_name,
output_var_args_name,
output_varname,
output_var_args_name);
} else {
const char* FWD_OUT_TENSOR_TEMPLATE =
" egr::EagerUtils::GetOutput(outs[\"%s\"][0], %s);\n"
" paddle::Tensor& %s = *%s;\n"
" (void)%s; // To avoid error: unused variable\n";
out_tensor_str = paddle::string::Sprintf(FWD_OUT_TENSOR_TEMPLATE,
output_name,
output_var_args_name,
output_varname,
output_var_args_name,
output_varname);
}
} else {
if (!forward_inplace_map.empty() &&
forward_inplace_map.count(output_name)) {
// Modify meta info of inplace tensor.
// Bump inplace version of inplace tensor.
auto inplace_input_name = forward_inplace_map[output_name];
const char* FWD_OUT_TENSOR_TEMPLATE =
" egr::EagerUtils::GetOutput(outs[\"%s\"][0], &%s);\n"
" %s.bump_inplace_version();\n"
" VLOG(3) << \"Tensor(\" << %s.name() << \") uses Inplace "
"Strategy.\";\n";
out_tensor_str =
paddle::string::Sprintf(FWD_OUT_TENSOR_TEMPLATE,
output_name,
LegalizeVarName(inplace_input_name),
LegalizeVarName(inplace_input_name),
LegalizeVarName(inplace_input_name));
} else {
const char* FWD_OUT_TENSOR_TEMPLATE =
" paddle::Tensor %s;\n"
" egr::EagerUtils::GetOutput(outs[\"%s\"][0], &%s);\n";
out_tensor_str = paddle::string::Sprintf(FWD_OUT_TENSOR_TEMPLATE,
output_varname,
output_name,
output_varname);
}
}
return_types[return_position] = "paddle::Tensor";
}
if (!forward_inplace_map.empty() &&
forward_inplace_map.count(output_name)) {
// Replace output directly with input in inplace op.
return_contents[return_position] =
LegalizeVarName(forward_inplace_map[output_name]);
} else {
return_contents[return_position] = output_varname;
}
trace_op_body_str += out_tensor_str;
}
if (!forward_inplace_map.empty()) {
trace_op_body_str +=
" egr::Controller::Instance().SetAMPLevel(current_level);\n";
}
trace_op_body_str += "\n";
VLOG(6) << "Converted Output VarBase to EagerVariable(s)";
/* ------ END Generate TraceOp ----- */
// [Generation] Handle core_ops_legacy_returns_info
// avoid inplace op changing core_ops_legacy_returns_info
if (core_ops_legacy_returns_info.empty() ||
!core_ops_legacy_returns_info.count(op_type)) {
core_ops_legacy_returns_info[op_type] = return_contents;
}
// [Generation] ComputeRequireGrad -> GradNodeCreation
if (!bwd_info.GenerateForwardOnly()) {
// If GradNode needs to be generated, pass `trace_op_body_str`
// into `GenerateGradNodeCreationContent`.
std::string grad_node_creation_body_str = GenerateGradNodeCreationContent(
fwd_info, bwd_info, trace_op_body_str, forward_inplace_map);
generated_function_body += grad_node_creation_body_str;
generated_function_body += "\n";
// [Generation] Call RetainGradForTensor
VLOG(6) << "Generated GradNode Creation codes";
} else {
// If GradNode doesn't need to be generated, generate TraceOP directly.
generated_function_body += trace_op_body_str;
}
// [Generation] Handle return: Tuple/Vector/Tensor
generated_function_body += "\n";
std::string return_str = "";
std::string return_type_str = "";
std::string function_proto_return_type_str = "";
if (return_contents.size() > 1) {
// Return tuple
std::string return_content_str = "";
for (const std::string& s : return_contents) {
return_content_str += s + ",";
}
return_content_str.pop_back(); // Remove trailing ","
for (const std::string& s : return_types) {
return_type_str += s + ",";
}
return_type_str.pop_back(); // Remove trailing ","
const char* FWD_TUPLE_RETURN_TEMPLATE = " return std::make_tuple(%s);";
return_str =
paddle::string::Sprintf(FWD_TUPLE_RETURN_TEMPLATE, return_content_str);
const char* FWD_FUNCTION_PROTO_RETURN_TEMPLATE = "std::tuple<%s>";
function_proto_return_type_str = paddle::string::Sprintf(
FWD_FUNCTION_PROTO_RETURN_TEMPLATE, return_type_str);
} else if (return_contents.size() == 1) {
// Return vector<Tensor> or Tensor
return_type_str = return_types[0];
const char* FWD_TENSOR_RETURN_TEMPLATE = " return %s;";
return_str =
paddle::string::Sprintf(FWD_TENSOR_RETURN_TEMPLATE, return_contents[0]);
function_proto_return_type_str = return_type_str;
} else {
return_str = "return nullptr;";
function_proto_return_type_str = "void*";
}
generated_function_body += return_str;
generated_function_body += "\n";
VLOG(6) << "Generated return codes";
// [Generation] Get Full Function
std::string function_name;
if (forward_inplace_map.empty()) {
function_name = op_type + "_dygraph_function";
} else {
// change function_name for inplace op.
function_name = op_type + "__dygraph_function";
}
if (!dygraph_function_args_str.empty()) {
auto iter = dygraph_function_args_str.begin();
if ((*iter) == ',') dygraph_function_args_str.erase(iter);
}
const char* DYGRAPH_FUNCTION_EVENT_RECORD_FUNCTION_TEMPLATE =
" phi::RecordEvent dygraph_entrance_record_event(\"%s\", "
"phi::TracerEventType::Operator, 1);";
std::string event_name = op_type + " dygraph";
std::string fwd_record_event_str = paddle::string::Sprintf(
DYGRAPH_FUNCTION_EVENT_RECORD_FUNCTION_TEMPLATE, event_name);
const char* FWD_FUNCTION_TEMPLATE =
"TEST_API %s %s(%s) {\n\n"
"%s\n"
"%s\n"
"}\n\n";
std::string fwd_function_str =
paddle::string::Sprintf(FWD_FUNCTION_TEMPLATE,
function_proto_return_type_str,
function_name,
dygraph_function_args_str,
fwd_record_event_str,
generated_function_body);
// [Generation] Generate forward functions header
const char* FWD_HEADER_TEMPLATE = "TEST_API %s %s(%s);\n";
std::string dygraph_function_declaration_str =
paddle::string::Sprintf(FWD_HEADER_TEMPLATE,
function_proto_return_type_str,
function_name,
dygraph_function_args_str);
return {fwd_function_str, dygraph_function_declaration_str};
}
static std::string GenerateSingleOpBase(
const std::string& fwd_op_type,
const std::string& op_base_type,
const std::unordered_map<std::string, size_t>& fwd_inputs_name_pos_map,
const std::unordered_map<std::string, size_t>& fwd_outputs_name_pos_map,
const std::vector<proto::OpProto::Var>& in_vars,
const std::map<std::string, std::string>& grad_ins_fwd_slotname_map,
const std::map<std::string, std::string>& grad_ins_grad_slotname_map,
const std::map<std::string, std::string>& grad_outs_slotname_map,
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
grad_ins,
const std::map<
std::string,
std::vector<std::shared_ptr<paddle::imperative::VariableWrapper>>>&
grad_outs,
const paddle::framework::AttributeMap& grad_attrs,
const std::unordered_map<std::string, std::string>& backward_inplace_map,
bool is_op_base_per_duplicable_input,
size_t* outs_size) {
std::string generated_grad_function_body = "";
const std::string& ins_name = "ins" + std::to_string(*outs_size);
const std::string& outs_name = "outs" + std::to_string(*outs_size);
const std::string& attrs_name = "attrs_map" + std::to_string(*outs_size);
const std::string& hooked_grads = "hooked_grads" + std::to_string(*outs_size);
// [Generation] Get Full Zero
std::string fill_zero_str = "";
if (ops_to_fill_zero_for_empty_grads.count(fwd_op_type)) {
for (auto const& iter : grad_ins) {
const std::string& grad_input_name = iter.first;
if (grad_ins_grad_slotname_map.count(grad_input_name)) {
size_t fwd_output_position = fwd_outputs_name_pos_map.at(
grad_ins_grad_slotname_map.at(grad_input_name));
const char* FILL_ZERO_TEMPLATE =
" egr::EagerUtils::FillZeroForEmptyOptionalGradInput(&grads[%d], "
"this->InputMeta()[%d]);\n";
fill_zero_str += paddle::string::Sprintf(
FILL_ZERO_TEMPLATE, fwd_output_position, fwd_output_position);
}
}
}
generated_grad_function_body += fill_zero_str;
generated_grad_function_body +=
" paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize> " +
hooked_grads + " = " + fwd_op_type +
"GradNodeCompat::ApplyGradientHooks(grads);\n";
// [Generation] Get Ins Map
std::unordered_set<std::string> dispensable_input_name_set;
for (const auto& in : in_vars) {
if (in.dispensable()) dispensable_input_name_set.insert(in.name());
}
std::unordered_set<std::string> duplicable_input_name_set;
for (const auto& in : in_vars) {
if (in.duplicable()) duplicable_input_name_set.insert(in.name());
}
const char* CHECK_BACKWARD_INPLACE_TEMPLATE =
" // Check backward inplace info\n"
" bool %s = false;\n"
" %s\n"
" if (%s.has_allocation()) {\n"
" VLOG(10) << %s.name() << \"(%s) use_count: \" << "
"%s.impl().use_count();\n"
" if (%s.impl().use_count() == 1 || (%s.impl().use_count() == 2 && "
"%s.impl().get() == %s.impl().get())) {\n"
" %s = true;\n"
" }\n"
" }\n";
const std::string& can_be_inplaced_name =
"can_be_inplaced" + std::to_string(*outs_size);
const std::string& bwd_inplace_input_name =
"backward_inplace_tensor" + std::to_string(*outs_size);
bool process_backward_inplace = false;
std::string ins_contents_str = "";
for (auto const& iter : grad_ins) {
const std::string& grad_input_name = iter.first;
if (grad_ins_fwd_slotname_map.count(grad_input_name)) {
// Fwd Tensor
const std::string& fwd_name =
grad_ins_fwd_slotname_map.at(grad_input_name);
if (dispensable_input_name_set.count(fwd_name)) {
continue;
}
std::string struct_fwd_input_name =
grad_ins_fwd_slotname_map.at(grad_input_name) + "_";
const char* GRAD_INS_FWD_CONTENT_TEMPLATE =
"{ \"%s\", "
"egr::EagerUtils::TrySyncToVars(egr::EagerUtils::"
"RecoverTensorWrapper("
"&"
"this->%s)) },";
ins_contents_str += paddle::string::Sprintf(GRAD_INS_FWD_CONTENT_TEMPLATE,
grad_input_name,
struct_fwd_input_name);
if (!backward_inplace_map.empty() &&
backward_inplace_map.count(grad_input_name)) {
process_backward_inplace = true;
const char* GRAD_INS_FWD_TENSOR_WRAPPER_TEMPLATE =
"auto %s = egr::EagerUtils::RecoverTensorWrapper(&this->%s);";
std::string tensor_wrapper_str =
paddle::string::Sprintf(GRAD_INS_FWD_TENSOR_WRAPPER_TEMPLATE,
bwd_inplace_input_name,
struct_fwd_input_name);
const char* GRAD_INS_FWD_TENSOR_TEMPLATE =
"(&this->%s)->get_intermediate_tensor()";
std::string tensor_wrapper_intermediate_tensor_str =
paddle::string::Sprintf(GRAD_INS_FWD_TENSOR_TEMPLATE,
struct_fwd_input_name);
generated_grad_function_body +=
paddle::string::Sprintf(CHECK_BACKWARD_INPLACE_TEMPLATE,
can_be_inplaced_name,
tensor_wrapper_str,
bwd_inplace_input_name,
bwd_inplace_input_name,
grad_input_name,
bwd_inplace_input_name,
bwd_inplace_input_name,
bwd_inplace_input_name,
bwd_inplace_input_name,
tensor_wrapper_intermediate_tensor_str,
can_be_inplaced_name);
}
} else if (grad_ins_grad_slotname_map.count(grad_input_name)) {
// Fwd Tensor's Grad
size_t fwd_output_position = fwd_outputs_name_pos_map.at(
grad_ins_grad_slotname_map.at(grad_input_name));
const char* GRAD_INS_GRAD_CONTENT_TEMPLATE =
"{ \"%s\", egr::EagerUtils::TrySyncToVars(%s[%d]) },";
ins_contents_str +=
paddle::string::Sprintf(GRAD_INS_GRAD_CONTENT_TEMPLATE,
grad_input_name,
hooked_grads,
fwd_output_position);
if (!backward_inplace_map.empty() &&
backward_inplace_map.count(grad_input_name)) {
process_backward_inplace = true;
const char* GRAD_INS_HOOKED_GRAD_TEMPLATE = "auto& %s = %s[%d][0];";
std::string hooked_grads_tensor_str =
paddle::string::Sprintf(GRAD_INS_HOOKED_GRAD_TEMPLATE,
bwd_inplace_input_name,
hooked_grads,
fwd_output_position);
const char* GRAD_INS_GRAD_TENSOR_TEMPLATE = "grads[%d][0]";
std::string grads_tensor_str = paddle::string::Sprintf(
GRAD_INS_GRAD_TENSOR_TEMPLATE, fwd_output_position);
generated_grad_function_body +=
paddle::string::Sprintf(CHECK_BACKWARD_INPLACE_TEMPLATE,
can_be_inplaced_name,
hooked_grads_tensor_str,
bwd_inplace_input_name,
bwd_inplace_input_name,
grad_input_name,
bwd_inplace_input_name,
bwd_inplace_input_name,
bwd_inplace_input_name,
bwd_inplace_input_name,
grads_tensor_str,
can_be_inplaced_name);
}
} else {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"Unable to find forward slot name that matches %s",
grad_input_name));
}
}
if (!ins_contents_str.empty())
ins_contents_str.pop_back(); // // Remove trailing ","
const char* BWD_INS_MAP_TEMPLATE =
" std::map<std::string, "
"std::vector<std::shared_ptr<egr::EagerVariable>>> %s = { "
"%s };\n";
std::string ins_map_str =
paddle::string::Sprintf(BWD_INS_MAP_TEMPLATE, ins_name, ins_contents_str);
generated_grad_function_body += ins_map_str;
for (auto const& iter : grad_ins) {
const std::string& grad_input_name = iter.first;
if (grad_ins_fwd_slotname_map.count(grad_input_name)) {
// Fwd Tensor
const std::string& fwd_name =
grad_ins_fwd_slotname_map.at(grad_input_name);
if (dispensable_input_name_set.count(fwd_name)) {
std::string struct_fwd_input_name =
grad_ins_fwd_slotname_map.at(grad_input_name) + "_";
if (duplicable_input_name_set.count(fwd_name)) {
const char* DISPENSABLE_GRAD_INS_FWD_CONTENT_TEMPLATE =
" if(this->%s.size() > 0) %s[\"%s\"] = "
"egr::EagerUtils::TrySyncToVars(egr::EagerUtils::"
"RecoverTensorWrapper(&this->%s));\n";
generated_grad_function_body +=
paddle::string::Sprintf(DISPENSABLE_GRAD_INS_FWD_CONTENT_TEMPLATE,
struct_fwd_input_name,
ins_name,
grad_input_name,
struct_fwd_input_name);
} else {
const char* DISPENSABLE_GRAD_INS_FWD_CONTENT_TEMPLATE =
" auto %s = egr::EagerUtils::RecoverTensorWrapper(&this->%s);\n"
" if(%s.defined()) %s[\"%s\"] = "
" egr::EagerUtils::TrySyncToVars(%s);\n";
generated_grad_function_body +=
paddle::string::Sprintf(DISPENSABLE_GRAD_INS_FWD_CONTENT_TEMPLATE,
grad_input_name,
struct_fwd_input_name,
grad_input_name,
ins_name,
grad_input_name,
grad_input_name);
}
}
}
}
VLOG(6) << "Generated Ins Map";
// [Generation] Get Outs Map
std::string outs_contents_str = "";
for (auto const& iter : grad_outs) {
const std::string& grad_output_name = iter.first;
if (grad_outs_slotname_map.count(grad_output_name)) {
// Fwd Tensor
const std::string& fwd_name = grad_outs_slotname_map.at(grad_output_name);
/* Handle Special Case: "PullSparseOp", etc
Forward:
Ids W
| |
PullSparseOp
|
Out
Backward:
Ids GradOut W
| | |
PullSparseGradOp
|
GradOut
Its grad output "GradOut" corresponds to forward output "Out",
where there is a hidden inplace involved. So we find "GradOut"'s
index
in
grads, and perform the inplace operation by constructing outs =
{{"Out", grads[i]}}
GradOut -> Out -> fwd_output_pos -> grads position -> grads[i]
outs = {{"Out", grads[i]}}
For returns, append "GradOut" to the very end of return list.
*/
if (!fwd_inputs_name_pos_map.count(fwd_name)) {
PADDLE_ENFORCE(fwd_outputs_name_pos_map.count(fwd_name),
common::errors::Fatal(
"fwd_name not found in fwd_inputs_name_pos_map nor "
"fwd_outputs_name_pos_map"));
size_t grads_position = fwd_outputs_name_pos_map.at(fwd_name);
const char* GRAD_OUTS_CONTENT_TEMPLATE =
" if((!out_metas[%d].empty()) && "
"(!(out_metas[%d][0].IsStopGradient()))){ %s.insert({ \"%s\", "
"egr::EagerUtils::TrySyncToVars(%s[%d])});}\n";
outs_contents_str += paddle::string::Sprintf(GRAD_OUTS_CONTENT_TEMPLATE,
grads_position,
grads_position,
outs_name,
grad_output_name,
hooked_grads,
grads_position);
} else {
if (dispensable_input_name_set.count(fwd_name) &&
grad_ins_fwd_slotname_map.count(fwd_name)) {
continue;
}
size_t fwd_input_position = fwd_inputs_name_pos_map.at(fwd_name);
if (duplicable_input_name_set.count(fwd_name) &&
!is_op_base_per_duplicable_input) {
const char* GRAD_OUTS_CONTENT_TEMPLATE =
" if(!out_metas[%d].empty()){ %s.insert({ \"%s\", "
"egr::EagerUtils::CreateVars(out_metas[%d].size())});}\n";
outs_contents_str +=
paddle::string::Sprintf(GRAD_OUTS_CONTENT_TEMPLATE,
fwd_input_position,
outs_name,
grad_output_name,
fwd_input_position);
} else {
const char* GRAD_OUTS_CONTENT_TEMPLATE =
" if((!out_metas[%d].empty()) && "
"(!(out_metas[%d][0].IsStopGradient()))){ %s.insert({ \"%s\", "
"{std::make_shared<egr::EagerVariable>(egr::Controller::Instance("
").GenerateUniqueName())}});}\n";
outs_contents_str +=
paddle::string::Sprintf(GRAD_OUTS_CONTENT_TEMPLATE,
fwd_input_position,
fwd_input_position,
outs_name,
grad_output_name);
}
}
} else {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"Unable to find forward slot name that matches %s",
grad_output_name));
}
}
const char* BWD_OUTS_MAP_TEMPLATE =
" std::map<std::string, "
"std::vector<std::shared_ptr<egr::EagerVariable>>> %s;\n";
std::string outs_map_str =
paddle::string::Sprintf(BWD_OUTS_MAP_TEMPLATE, outs_name);
generated_grad_function_body += outs_map_str;
generated_grad_function_body += outs_contents_str;
generated_grad_function_body += "\n";
for (auto const& iter : grad_outs) {
const std::string& grad_output_name = iter.first;
if (grad_outs_slotname_map.count(grad_output_name)) {
// Fwd Tensor
const std::string& fwd_name = grad_outs_slotname_map.at(grad_output_name);
if (fwd_inputs_name_pos_map.count(fwd_name)) {
if (dispensable_input_name_set.count(fwd_name) &&
grad_ins_fwd_slotname_map.count(fwd_name)) {
if (duplicable_input_name_set.count(fwd_name) &&
!is_op_base_per_duplicable_input) {
size_t fwd_input_position = fwd_inputs_name_pos_map.at(fwd_name);
const char* DISPENSABLE_GRAD_OUTS_FWD_CONTENT_TEMPLATE =
" if((%s.size() > 0) && (!out_metas[%d].empty()) && "
"(!out_metas[%d][0].IsStopGradient())) %s[\"%s\"] = "
"egr::EagerUtils::CreateVars( "
"out_metas[%d].size() );\n";
generated_grad_function_body += paddle::string::Sprintf(
DISPENSABLE_GRAD_OUTS_FWD_CONTENT_TEMPLATE,
fwd_name,
outs_name,
grad_output_name,
fwd_input_position);
} else {
size_t fwd_input_position = fwd_inputs_name_pos_map.at(fwd_name);
const char* DISPENSABLE_GRAD_OUTS_FWD_CONTENT_TEMPLATE =
" if(%s.defined() && (!out_metas[%d].empty()) && "
"(!out_metas[%d][0].IsStopGradient())) %s[\"%s\"] = "
"{std::make_shared<egr::EagerVariable>(egr::Controller::"
"Instance().GenerateUniqueName())};\n";
generated_grad_function_body += paddle::string::Sprintf(
DISPENSABLE_GRAD_OUTS_FWD_CONTENT_TEMPLATE,
fwd_name,
fwd_input_position,
fwd_input_position,
outs_name,
grad_output_name);
}
}
}
} else {
PADDLE_THROW(common::errors::Fatal(
"Detected mismatched slot names."
"Unable to find forward slot name that matches %s",
grad_output_name));
}
}
VLOG(6) << "Generated Outs Map";
// [Generation] Process Backward Inplace
if (process_backward_inplace) {
const char* HANDLE_BACKWARD_INPLACE_BETWEEN_INPUT_AND_OUTPUT =
" if (%s && %s.count(\"%s\") && %s.count(\"%s\")) {\n"
" egr::EagerUtils::HandleViewBetweenInputAndOutput(%s[\"%s\"][0], "
"%s[\"%s\"][0]);\n"
" };\n";
std::string backward_inplace_map_str = "";
for (auto const& iter : backward_inplace_map) {
std::string backward_inplace_input_name = iter.first;
std::string backward_inplace_output_name = iter.second;
backward_inplace_map_str += paddle::string::Sprintf(
HANDLE_BACKWARD_INPLACE_BETWEEN_INPUT_AND_OUTPUT,
can_be_inplaced_name,
ins_name,
backward_inplace_input_name,
outs_name,
backward_inplace_output_name,
ins_name,
backward_inplace_input_name,
outs_name,
backward_inplace_output_name);
}
generated_grad_function_body += backward_inplace_map_str;
VLOG(6) << "Process Backward Inplace";
}
// [Generation] Get Attrs Map
const char* ATTRS_TEMPLATE = " auto& %s = this->attr_map_;\n";
std::string grad_attrs_str =
paddle::string::Sprintf(ATTRS_TEMPLATE, attrs_name);
if (fwd_op_type == "cast") {
// switch in out dtype
const char* CAST_GRAD =
" auto temp_type = %s[\"in_dtype\"];\n"
" %s[\"in_dtype\"] = %s[\"out_dtype\"];\n"
" %s[\"out_dtype\"] = temp_type;\n";
grad_attrs_str += paddle::string::Sprintf(
CAST_GRAD, attrs_name, attrs_name, attrs_name, attrs_name);
}
// Handle dynamic grad attributes
grad_attrs_str += HandleDynamicGradAttributes(fwd_op_type, attrs_name);
generated_grad_function_body += grad_attrs_str;
const char* TRACE_OP_TEMPLATE =
" // Pass the entire attribute map to TraceOp\n"
" // The underlying kernel will pickup whatever attribute they need "
"at runtime\n"
" egr::Controller::Instance().GetCurrentTracer()->TraceOp(\"%s\", %s, "
"%s, %s,\n"
" egr::Controller::Instance().GetExpectedPlace(),\n"
" &this->default_attr_map_, false, {});\n";
std::string trace_opbase_str = paddle::string::Sprintf(
TRACE_OP_TEMPLATE, op_base_type, ins_name, outs_name, attrs_name);
generated_grad_function_body += trace_opbase_str;
VLOG(6) << "Generated Attrs Map";
// [Generation] Get Return
std::string outputs_str = "";
size_t num_appended_outputs = 0;
for (auto const& iter : grad_outs) {
const std::string& grad_out_name = iter.first;
const std::string& fwd_name = grad_outs_slotname_map.at(grad_out_name);
if (fwd_inputs_name_pos_map.count(fwd_name)) {
size_t fwd_input_position = fwd_inputs_name_pos_map.at(fwd_name);
if (!is_op_base_per_duplicable_input) {
const char* BWD_OUTPUT_TEMPLATE =
" if (%s.find(\"%s\") != %s.end()) { outputs[%d] = "
"egr::EagerUtils::GetOutputs(%s[\"%s\"]); }\n";
outputs_str += paddle::string::Sprintf(BWD_OUTPUT_TEMPLATE,
outs_name,
grad_out_name,
outs_name,
fwd_input_position,
outs_name,
grad_out_name);
} else {
const char* BWD_OUTPUT_TEMPLATE =
" "
"if (%s.find(\"%s\") != %s.end()) { "
"outputs[0].emplace_back(egr::EagerUtils::GetOutputs(%s[\"%s\"])[0]"
"); }\n";
outputs_str += paddle::string::Sprintf(BWD_OUTPUT_TEMPLATE,
outs_name,
grad_out_name,
outs_name,
outs_name,
grad_out_name);
}
num_appended_outputs++;
} else {
PADDLE_ENFORCE(fwd_outputs_name_pos_map.count(fwd_name),
common::errors::Fatal(
"fwd_name not found in fwd_inputs_name_pos_map nor "
"fwd_outputs_name_pos_map"));
}
}
/* Handle Special Case: "PullSparseOp", etc
For returns, append "GradOut" to the very end of return list. */
for (auto const& iter : grad_outs) {
const std::string& grad_out_name = iter.first;
const std::string& fwd_name = grad_outs_slotname_map.at(grad_out_name);
if (fwd_outputs_name_pos_map.count(fwd_name)) {
const char* BWD_OUTPUT_TEMPLATE =
" if (%s.find(\"%s\") != %s.end()) { outputs[%d] = "
"egr::EagerUtils::GetOutputs(%s[\"%s\"]); }\n";
outputs_str += paddle::string::Sprintf(BWD_OUTPUT_TEMPLATE,
outs_name,
grad_out_name,
outs_name,
num_appended_outputs,
outs_name,
grad_out_name);
num_appended_outputs++;
}
}
generated_grad_function_body += outputs_str;
generated_grad_function_body += "\n";
*outs_size += grad_outs.size();
return generated_grad_function_body;
}
/* ---------------------------------------------- */
/* --------- CodeGen: GradNode::operator() ------ */
/* ---------------------------------------------- */
static std::string GenerateGradNodeCCContents(
const ForwardGenerationInfo& fwd_info,
const GradNodeGenerationInfo& bwd_info) {
/* --- Process Forward Info --- */
const std::string& fwd_op_type = fwd_info.GetOpType();
const std::unordered_map<std::string, size_t>& fwd_inputs_name_pos_map =
fwd_info.GetFwdInputsNamePosMap();
const std::unordered_map<std::string, size_t>& fwd_outputs_name_pos_map =
fwd_info.GetFwdOutputsNamePosMap();
const std::vector<proto::OpProto::Var>& in_vars = fwd_info.GetInVars();
const std::vector<proto::OpProto::Var>& out_vars = fwd_info.GetOutVars();
VLOG(6) << "Generating Grad Node CC";
/* [Outline]
vector<vector<Tensor>> GradNodeXXX::operator()(vector<vector<Tensor>>& grads)
{
const std::shared_ptr<Tracer>& tracer = imperative::GetCurrentTracer();
// Comes from "grad_ins"
std::map<std::string, std::vector<std::shared_ptr<VarBase>>> ins =
{
"X" : this->"X", "Y" : this->"Y",
"Out0@Grad":
TrySyncToVars(hooked_grads["fwd_outputs_name_pos_map[grad_ins_grad_slotname_map["Out0@Grad"]]"]),
"Out1@Grad":
TensorsToVarBases(hooked_grads["fwd_outputs_name_pos_map[grad_ins_grad_slotname_map["Out1@Grad"]]"])
};
// Comes from "grad_outs"
std::map<std::string, std::vector<std::shared_ptr<VarBase>>> outs =
{
"X@Grad" :
CreateVars(this->OutputMeta()["fwd_inputs_name_pos_map[grad_outs_slotname_map["X@Grad"]]"].Size()),
"Y@Grad" :
CreateVars(this->OutputMeta()["fwd_inputs_name_pos_map[grad_outs_slotname_map["Y@Grad"]]"].Size())
};
// Visit each OpBase
for(auto iter = "grad_node->begin()"; iter < "grad_node->end()"; iter++) {
// Simply pass entire attribute map to kernels
Controller.Instance().GetCurrentTracer()->TraceOp("iter->Type()", ins,
outs, this->attr_map_,
egr::Controller::Instance().ExpectedPlace(), false, {});
}
vector<vector<paddle::Tensor>> outputs(outs.size());
for(auto& kv : outs) {
outputs["fwd_inputs_name_pos_map[grad_outs_slotname_map[kv.first]]"] =
GetOutputs(outs["kv.first"]);
}
return outputs;
}
*/
const char* EAGER_LOG_TEMPLATE =
" VLOG(3) << \"Running Eager Backward Node: %sGradNodeCompat\";\n";
std::string generated_grad_function_body =
paddle::string::Sprintf(EAGER_LOG_TEMPLATE, fwd_op_type);
// This is a Copy
auto op_base_infos = bwd_info.GetOpBaseInfos();
/* Special Case: ops such as sum_grad_op is implemented abnormally,
where it unpacked duplicable GradX and created one OpBase
corresponds to each member of GradX[i]
*/
bool is_op_base_per_duplicable_input = false;
if (in_vars.size() == 1 && out_vars.size() == 1 && in_vars[0].duplicable() &&
!out_vars[0].duplicable() &&
op_base_infos.size() == NUM_CREATED_DUP_INPUTS) {
is_op_base_per_duplicable_input = true;
// Only keep the first op_base
auto op_base_info = op_base_infos[0];
op_base_infos.clear();
op_base_infos.emplace_back(std::move(op_base_info));
}
size_t outs_size = 0;
for (const auto& op_base_info : op_base_infos) {
const auto& grad_ins_fwd_slotname_map =
op_base_info.GetGradInsFwdSlotnameMap();
const auto& grad_ins_grad_slotname_map =
op_base_info.GetGradInsGradSlotnameMap();
const auto& grad_outs_slotname_map = op_base_info.GetGradOutsSlotnameMap();
const auto& grad_ins = op_base_info.GetGradIns();
const auto& grad_outs = op_base_info.GetGradOuts();
const auto& grad_attrs = op_base_info.GetGradAttrs();
const auto& backward_inplace_map = op_base_info.GetBackwardInplaceMap();
const std::string& op_base_type = op_base_info.GetOpBaseType();
generated_grad_function_body +=
GenerateSingleOpBase(fwd_op_type,
op_base_type,
fwd_inputs_name_pos_map,
fwd_outputs_name_pos_map,
in_vars,
grad_ins_fwd_slotname_map,
grad_ins_grad_slotname_map,
grad_outs_slotname_map,
grad_ins,
grad_outs,
grad_attrs,
backward_inplace_map,
is_op_base_per_duplicable_input,
&outs_size);
}
if (is_op_base_per_duplicable_input) {
const char* OP_BASE_PER_DUP_INPUT_TEMPLATE =
" for(size_t i = 0; i < this->OutputMeta()[0].size(); i++) {\n"
" %s\n"
" }\n";
generated_grad_function_body = paddle::string::Sprintf(
OP_BASE_PER_DUP_INPUT_TEMPLATE, generated_grad_function_body);
}
const char* BWD_RETURN_TEMPLATE =
" const auto& out_metas = OutputMeta();\n"
" paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize> outputs(%d);\n"
"%s\n"
" if(NeedComplexToRealConversion()) "
"HandleComplexGradToRealGrad(&outputs);\n"
" return outputs;\n";
generated_grad_function_body = paddle::string::Sprintf(
BWD_RETURN_TEMPLATE, in_vars.size(), generated_grad_function_body);
// [Generation] Get Full Grad Function
const char* GRAD_FUNCTION_TEMPLATE =
"paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize> "
"%sGradNodeCompat::operator()("
"paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize>& grads, bool "
"create_graph, bool is_new_grad) {\n"
"%s"
"\n}";
std::string grad_function_str = paddle::string::Sprintf(
GRAD_FUNCTION_TEMPLATE, fwd_op_type, generated_grad_function_body);
VLOG(6) << "Generated returns";
return grad_function_str;
}
/* ----------------------------------------- */
/* --------- CodeGen: GradNode Header ------ */
/* ----------------------------------------- */
static std::string GenerateGradNodeHeaderContents(
const ForwardGenerationInfo& fwd_info,
const GradNodeGenerationInfo& bwd_info) {
const std::string& op_type = fwd_info.GetOpType();
const std::vector<proto::OpProto::Var>& in_vars = fwd_info.GetInVars();
const std::vector<proto::OpProto::Var>& out_vars = fwd_info.GetOutVars();
const auto& op_base_infos = bwd_info.GetOpBaseInfos();
VLOG(6) << "Generating Grad Node Header";
const char* GRAD_NODE_TEMPLATE =
"class %sGradNodeCompat : public egr::GradNodeBase {\n"
" public:\n"
" %sGradNodeCompat() : egr::GradNodeBase() { VLOG(7) << \" Construct "
"%sGradNodeCompat \"; }\n"
" %sGradNodeCompat(size_t bwd_in_slot_num, size_t bwd_out_slot_num) : "
"egr::GradNodeBase(bwd_in_slot_num, bwd_out_slot_num) { VLOG(7) << \" "
"Construct %sGradNodeCompat \"; }\n"
" ~%sGradNodeCompat() override { VLOG(6) << \" Destruct "
"%sGradNodeCompat \"; }\n"
"\n"
" virtual "
"paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize> "
"operator()("
"paddle::small_vector<std::vector<paddle::Tensor>, "
"egr::kSlotSmallVectorSize>& grads, bool "
"create_graph = false, bool is_new_grad = false) "
"override;\n"
"\n"
" void ClearTensorWrappers() override {\n"
"%s\n"
" SetIsTensorWrappersCleared(true);\n"
" }\n"
" std::string name() override { return \"%sGradNodeCompat\"; }\n"
"\n"
"std::shared_ptr<GradNodeBase> Copy() const override {{\n"
" auto copied_node = std::shared_ptr<%sGradNodeCompat>(new "
"%sGradNodeCompat(*this));\n"
" return copied_node;\n"
"}}\n"
"\n"
" // SetX, SetY, ...\n"
"%s\n"
" // SetAttrMap\n"
"%s\n"
" private:\n"
" // TensorWrappers\n"
"%s\n"
" // Attribute Map\n"
"%s\n"
"};";
// [Generation] Handle Attributes
std::string set_attr_map_str =
" void SetAttrMap(paddle::framework::AttributeMap&& attr_map) {\n "
"attr_map_ = std::move(attr_map);\n }\n";
set_attr_map_str +=
" void SetDefaultAttrMap(paddle::framework::AttributeMap&& "
"default_attr_map) {\n default_attr_map_ = "
"std::move(default_attr_map);\n }\n";
std::string attr_members_str =
" paddle::framework::AttributeMap attr_map_;\n";
attr_members_str += " paddle::framework::AttributeMap default_attr_map_;";
VLOG(6) << "Generated SetAttr";
// [Generation] Handle TensorWrappers
std::unordered_set<std::string> duplicable_tensors;
for (const proto::OpProto::Var& input : in_vars) {
if (input.duplicable()) {
duplicable_tensors.insert(input.name());
}
}
for (const proto::OpProto::Var& output : out_vars) {
if (output.duplicable()) {
duplicable_tensors.insert(output.name());
}
}
std::string set_tensor_wrappers_str = "";
std::string tensor_wrapper_members_str = "";
std::string clear_tensor_wrappers_str = "";
for (const auto& iter : op_base_infos) {
const std::map<std::string, std::string>& grad_ins_fwd_slotname_map =
iter.GetGradInsFwdSlotnameMap();
const std::unordered_set<std::string>& no_need_buffer_ins =
iter.GetNoNeedBufferInputs();
for (const auto& kv : grad_ins_fwd_slotname_map) {
const std::string& tensor_wrapper_name = kv.second;
const std::string& struct_tensor_wrapper_name = kv.second + "_";
std::string tensor_wrapper_arg_str;
std::string tensor_wrapper_body_str;
std::string no_need_buffer_str = "false";
if (no_need_buffer_ins.count(tensor_wrapper_name)) {
no_need_buffer_str = "true";
}
if (duplicable_tensors.count(tensor_wrapper_name)) {
const char* ATTR_TENSOR_WRAPPER_ARG_TEMPLATE =
"const std::vector<paddle::Tensor>& %s";
tensor_wrapper_arg_str = paddle::string::Sprintf(
ATTR_TENSOR_WRAPPER_ARG_TEMPLATE, tensor_wrapper_name);
const char* TENSOR_WRAPPER_MEMBER_TEMPLATE =
" std::vector<egr::TensorWrapper> %s;\n";
tensor_wrapper_members_str += paddle::string::Sprintf(
TENSOR_WRAPPER_MEMBER_TEMPLATE, struct_tensor_wrapper_name);
const char* SET_TENSOR_WRAPPER_BODY_TEMPLATE =
"for(const auto& eager_tensor : %s) {\n"
" %s.emplace_back( egr::TensorWrapper(eager_tensor "
", %s) );\n"
" }\n";
tensor_wrapper_body_str =
paddle::string::Sprintf(SET_TENSOR_WRAPPER_BODY_TEMPLATE,
tensor_wrapper_name,
struct_tensor_wrapper_name,
no_need_buffer_str);
const char* CLEAR_TENSOR_WRAPPER_TEMPLATE =
"for (auto tw: %s) {\n"
" tw.clear();\n"
" }\n";
clear_tensor_wrappers_str += paddle::string::Sprintf(
CLEAR_TENSOR_WRAPPER_TEMPLATE, struct_tensor_wrapper_name);
} else {
const char* ATTR_TENSOR_WRAPPER_ARG_TEMPLATE =
"const paddle::Tensor& %s";
tensor_wrapper_arg_str = paddle::string::Sprintf(
ATTR_TENSOR_WRAPPER_ARG_TEMPLATE, tensor_wrapper_name);
const char* TENSOR_WRAPPER_MEMBER_TEMPLATE =
" egr::TensorWrapper %s;\n";
tensor_wrapper_members_str += paddle::string::Sprintf(
TENSOR_WRAPPER_MEMBER_TEMPLATE, struct_tensor_wrapper_name);
const char* SET_TENSOR_WRAPPER_BODY_TEMPLATE =
"%s = egr::TensorWrapper(%s, %s);\n";
tensor_wrapper_body_str =
paddle::string::Sprintf(SET_TENSOR_WRAPPER_BODY_TEMPLATE,
struct_tensor_wrapper_name,
tensor_wrapper_name,
no_need_buffer_str);
const char* CLEAR_TENSOR_WRAPPER_TEMPLATE = " %s.clear();\n";
clear_tensor_wrappers_str += paddle::string::Sprintf(
CLEAR_TENSOR_WRAPPER_TEMPLATE, struct_tensor_wrapper_name);
}
const char* SET_TENSOR_WRAPPER_TEMPLATE =
" void SetTensorWrapper_%s(%s) {\n %s\n }\n";
set_tensor_wrappers_str +=
paddle::string::Sprintf(SET_TENSOR_WRAPPER_TEMPLATE,
tensor_wrapper_name,
tensor_wrapper_arg_str,
tensor_wrapper_body_str);
}
}
VLOG(6) << "Generated TensorWrapper";
std::string grad_node_str =
paddle::string::Sprintf(GRAD_NODE_TEMPLATE,
op_type,
op_type,
op_type,
op_type,
op_type,
op_type,
op_type,
clear_tensor_wrappers_str,
op_type,
op_type,
op_type,
set_tensor_wrappers_str,
set_attr_map_str,
tensor_wrapper_members_str,
attr_members_str);
return grad_node_str;
}
/* --------------------------------- */
/* --------- FileGeneration --------- */
/* ---------------------------------- */
static std::string GenerateDygraphHFileIncludes() {
std::string dygraph_forward_api_includes_str =
"#pragma once\n"
"#include \"glog/logging.h\"\n"
"#include \"paddle/fluid/eager/autograd_meta.h\"\n"
"#include \"paddle/phi/core/memory/stats.h\"\n"
"#include \"paddle/phi/api/all.h\"\n"
"#include \"paddle/fluid/eager/utils.h\"\n"
"#include \"paddle/fluid/imperative/tracer.h\"\n"
"#include \"paddle/fluid/framework/op_registry.h\"\n"
"#include "
"\"paddle/fluid/eager/api/manual/fluid_manual/"
"dygraph_forward_api.h\"\n\n";
dygraph_forward_api_includes_str +=
"extern std::unordered_map<std::string, std::vector<std::string>> "
"core_ops_legacy_args_info;\n";
dygraph_forward_api_includes_str +=
"extern std::unordered_map<std::string, std::vector<std::string>> "
"core_ops_legacy_args_type_info;\n";
dygraph_forward_api_includes_str +=
"extern std::unordered_map<std::string, std::vector<std::string>> "
"core_ops_legacy_returns_info;\n\n";
return dygraph_forward_api_includes_str;
}
static void GenerateForwardHFile(const std::string& dygraph_forward_api_path,
const std::string& dygraph_forward_api_str) {
std::ofstream forward_header_stream(dygraph_forward_api_path, std::ios::out);
forward_header_stream << dygraph_forward_api_str;
forward_header_stream.close();
}
static void GenerateForwardDygraphFile(const std::string& forward_cc_path,
const std::string& fwd_function_str) {
const char* FORWARD_INCLUDE_TEMPLATE =
"#include "
"\"paddle/fluid/eager/api/generated/fluid_generated/"
"dygraph_forward_api.h\"\n"
"#include "
"\"paddle/fluid/eager/api/generated/fluid_generated/nodes/nodes.h\"\n"
"#include \"paddle/fluid/eager/api/utils/global_utils.h\"\n"
"#include \"paddle/fluid/imperative/amp_utils.h\"\n"
"#include \"paddle/fluid/eager/amp_auto_cast.h\"\n"
"#include \"paddle/phi/core/platform/profiler/event_tracing.h\"\n\n";
std::string forward_cc_include_str =
paddle::string::Sprintf(FORWARD_INCLUDE_TEMPLATE);
std::ofstream forward_cc_stream(forward_cc_path, std::ios::out);
forward_cc_stream << forward_cc_include_str;
forward_cc_stream << fwd_function_str;
forward_cc_stream.close();
}
static void GenerateNodeHFile(const std::string& node_h_path,
const std::string& grad_node_str) {
std::string node_h_include_str =
"#pragma once\n"
"#include \"paddle/fluid/eager/tensor_wrapper.h\"\n"
"#include \"paddle/fluid/imperative/tracer.h\"\n"
"#include \"paddle/fluid/eager/grad_node_info.h\"\n"
"#include "
"\"paddle/fluid/eager/api/manual/fluid_manual/nodes/nodes.h\"\n\n";
std::ofstream node_h_stream(node_h_path, std::ios::out);
node_h_stream << node_h_include_str;
node_h_stream << grad_node_str;
node_h_stream.close();
}
static void GenerateNodeCCFile(const std::string& node_cc_path,
const std::string& grad_function_str) {
const char* NODE_CC_INCLUDE_TEMPLATE =
"#include \"glog/logging.h\"\n"
"#include \"paddle/phi/api/all.h\"\n"
"#include \"paddle/fluid/imperative/tracer.h\"\n"
"#include \"paddle/fluid/framework/op_registry.h\"\n"
"#include \"paddle/fluid/eager/utils.h\"\n"
"#include \"paddle/fluid/eager/api/utils/global_utils.h\"\n"
"#include "
"\"paddle/fluid/eager/api/generated/fluid_generated/nodes/nodes.h\"\n\n";
std::string node_cc_include_str =
paddle::string::Sprintf(NODE_CC_INCLUDE_TEMPLATE);
std::ofstream node_cc_stream(node_cc_path, std::ios::out);
node_cc_stream << node_cc_include_str;
node_cc_stream << grad_function_str;
node_cc_stream.close();
}
static std::string ConvertCoreOpsInfosToString(
const std::unordered_map<std::string, std::vector<std::string>>&
core_ops_info) {
std::string core_ops_legacy_returns_info_init_str = "";
for (const auto& iter : core_ops_info) {
const char* Core_Ops_Returns_TEMPLATE = "{ \"%s\", { %s } },\n";
const std::string& op_type = iter.first;
std::string returns_str = "";
for (const auto& vector_iter : iter.second) {
returns_str += "\"" + vector_iter + "\" ,";
}
// Remove trailing ','
if (!returns_str.empty()) returns_str.pop_back();
std::string op_type_init_str = paddle::string::Sprintf(
Core_Ops_Returns_TEMPLATE, op_type, returns_str);
core_ops_legacy_returns_info_init_str += op_type_init_str;
}
// Remove trailing ','
if (!core_ops_legacy_returns_info_init_str.empty())
core_ops_legacy_returns_info_init_str.pop_back();
return core_ops_legacy_returns_info_init_str;
}
static std::string GenerateCoreOpsArgsInfo() {
const char* Core_Ops_Returns_MAP_TEMPLATE =
"std::unordered_map<std::string, std::vector<std::string>> "
"core_ops_legacy_args_info = { %s };\n";
std::string core_ops_args_info_init_str =
ConvertCoreOpsInfosToString(core_ops_legacy_args_info);
std::string core_ops_info_str = paddle::string::Sprintf(
Core_Ops_Returns_MAP_TEMPLATE, core_ops_args_info_init_str);
return core_ops_info_str;
}
static std::string GenerateCoreOpsArgsTypeInfo() {
const char* Core_Ops_Returns_MAP_TEMPLATE =
"std::unordered_map<std::string, std::vector<std::string>> "
"core_ops_legacy_args_type_info = { %s };\n";
std::string core_ops_args_type_info_init_str =
ConvertCoreOpsInfosToString(core_ops_legacy_args_type_info);
std::string core_ops_info_str = paddle::string::Sprintf(
Core_Ops_Returns_MAP_TEMPLATE, core_ops_args_type_info_init_str);
return core_ops_info_str;
}
static std::string GenerateCoreOpsReturnsInfo() {
const char* Core_Ops_Returns_MAP_TEMPLATE =
"std::unordered_map<std::string, std::vector<std::string>> "
"core_ops_legacy_returns_info = { %s };\n";
std::string core_ops_legacy_returns_info_init_str =
ConvertCoreOpsInfosToString(core_ops_legacy_returns_info);
std::string core_ops_info_str = paddle::string::Sprintf(
Core_Ops_Returns_MAP_TEMPLATE, core_ops_legacy_returns_info_init_str);
return core_ops_info_str;
}
static void DygraphCodeGeneration(const std::string& output_dir,
int split_count) {
std::string dygraph_forward_api_str = GenerateDygraphHFileIncludes();
std::string fwd_function_str = "";
std::string grad_node_h_str = "";
std::string grad_node_cc_str = "";
auto& op_info_map = paddle::framework::OpInfoMap::Instance().map();
paddle::flat_hash_map<std::string, OpInfo> op_info_map_need_gen;
for (auto& pair : op_info_map) {
const OpInfo& op_info = pair.second;
proto::OpProto* op_proto = op_info.proto_;
if (!CheckOpProto(op_proto)) continue;
const std::string& op_type = op_proto->type();
if (black_ops_list.count(op_type)) {
continue;
}
// Skip the sparse op
if (op_type.compare(0, 7, "sparse_") == 0 && op_type != "sparse_momentum" &&
op_type != "sparse_attention") {
continue;
}
GradNodeGenerationInfo bwd_info;
bool is_available = CollectGradInformationFromOpInfo(op_info, &bwd_info);
if (!is_available && !bwd_info.GenerateForwardOnly()) {
VLOG(6) << "Skipped operator: " << op_type;
continue;
}
op_info_map_need_gen.emplace(pair);
}
int each_cc_file_api_size =
static_cast<int>(op_info_map_need_gen.size() / split_count);
if (op_info_map_need_gen.size() % split_count != 0) {
each_cc_file_api_size++;
}
int api_index = 0;
int file_index = 0;
for (auto& pair : op_info_map_need_gen) {
const OpInfo& op_info = pair.second;
proto::OpProto* op_proto = op_info.proto_;
const std::string& op_type = op_proto->type();
/* ----------------------------- */
/* ---- Collect Information ---- */
/* ----------------------------- */
ForwardGenerationInfo fwd_info;
GradNodeGenerationInfo bwd_info;
VLOG(6) << "-------- CollectInformationFromOpInfo -------";
CollectForwardInformationFromOpInfo(op_info, &fwd_info);
CollectGradInformationFromOpInfo(op_info, &bwd_info);
VLOG(6) << "-------- PurifyOpProto -------";
PurifyForwardOpProto(*op_proto, &fwd_info);
if (!bwd_info.GenerateForwardOnly()) {
PurifyGradNodeGenerationInfo(*op_proto, &bwd_info);
}
/* --------------------------- */
/* --------- CodeGen --------- */
/* --------------------------- */
VLOG(6) << "-------- GenerateForwardFunctionContents -------";
std::pair<std::string, std::string> body_and_declaration =
GenerateForwardFunctionContents(fwd_info, bwd_info, {});
fwd_function_str += body_and_declaration.first + "\n";
VLOG(6) << "-------- GenerateDygraphForwardAPIContents -------";
std::string fwd_function_declare_str = body_and_declaration.second;
dygraph_forward_api_str += fwd_function_declare_str;
auto& infer_inplace =
paddle::framework::OpInfoMap::Instance().Get(op_type).infer_inplace_;
std::map<std::string, std::string> forward_inplace_map;
// Inplace Function Generator.
// `sum` op has duplicate input. Don't consider adding inplace strategy
// for `sum` in temporary.
if (infer_inplace && !special_inplace_op_set.count(op_type)) {
auto in_to_outs = infer_inplace(true);
for (auto& inplace_pair : in_to_outs) {
forward_inplace_map[inplace_pair.second] = inplace_pair.first;
}
VLOG(6) << "-------- GenerateInplaceForwardFunctionContents -------";
std::pair<std::string, std::string> inplace_body_and_declaration =
GenerateForwardFunctionContents(
fwd_info, bwd_info, forward_inplace_map);
fwd_function_str += inplace_body_and_declaration.first + "\n";
VLOG(6) << "-------- GenerateInplaceDygraphForwardAPIContents -------";
std::string inplace_fwd_function_declare_str =
inplace_body_and_declaration.second;
dygraph_forward_api_str += inplace_fwd_function_declare_str;
}
if (!bwd_info.GenerateForwardOnly()) {
VLOG(6) << "-------- GenerateGradNodeHeaderContents -------";
grad_node_h_str += GenerateGradNodeHeaderContents(fwd_info, bwd_info);
grad_node_h_str += "\n";
VLOG(6) << "-------- GenerateGradNodeCCContents -------";
grad_node_cc_str += GenerateGradNodeCCContents(fwd_info, bwd_info);
grad_node_cc_str += "\n";
}
VLOG(6) << op_type << ": Finished Generating Op: " << op_type;
api_index++;
if (api_index / each_cc_file_api_size > file_index) {
file_index++;
VLOG(6) << "-------- GenerateDygraphForwardCCFile -------";
std::string forward_cc_path = output_dir +
"/forwards/dygraph_forward_functions" +
std::to_string(file_index) + ".tmp.cc";
fwd_function_str += "\n";
GenerateForwardDygraphFile(forward_cc_path, fwd_function_str);
fwd_function_str = "";
VLOG(6) << "-------- GenerateNodeCCFile -------";
std::string node_cc_path =
output_dir + "/nodes/nodes" + std::to_string(file_index) + ".tmp.cc";
GenerateNodeCCFile(node_cc_path, grad_node_cc_str);
grad_node_cc_str = "";
}
}
file_index++;
VLOG(6) << "-------- GenerateDygraphForwardCCFile -------";
std::string forward_cc_path = output_dir +
"/forwards/dygraph_forward_functions" +
std::to_string(file_index) + ".tmp.cc";
GenerateForwardDygraphFile(forward_cc_path, fwd_function_str);
fwd_function_str = "";
GenerateForwardDygraphFile(
output_dir + "/forwards/dygraph_forward_functions_args_info.tmp.cc",
GenerateCoreOpsArgsInfo());
GenerateForwardDygraphFile(
output_dir + "/forwards/dygraph_forward_functions_args_type_info.tmp.cc",
GenerateCoreOpsArgsTypeInfo());
GenerateForwardDygraphFile(
output_dir + "/forwards/dygraph_forward_functions_returns_info.tmp.cc",
GenerateCoreOpsReturnsInfo());
VLOG(6) << "-------- GenerateNodeCCFile -------";
std::string node_cc_path =
output_dir + "/nodes/nodes" + std::to_string(file_index) + ".tmp.cc";
GenerateNodeCCFile(node_cc_path, grad_node_cc_str);
grad_node_cc_str = "";
VLOG(6) << "-------- GenerateForwardHFile -------";
std::string dygraph_forward_api_path =
output_dir + "/dygraph_forward_api.tmp.h";
GenerateForwardHFile(dygraph_forward_api_path, dygraph_forward_api_str);
VLOG(6) << "-------- GenerateNodeHFile -------";
std::string node_h_path = output_dir + "/nodes/nodes.tmp.h";
GenerateNodeHFile(node_h_path, grad_node_h_str);
}
} // namespace paddle::framework
std::map<std::string, std::pair<std::string, std::string>> view_op_map = {
{"squeeze2", {"X", "Out"}},
{"unsqueeze2", {"X", "Out"}},
{"reshape2", {"X", "Out"}},
{"flatten_contiguous_range", {"X", "Out"}},
};
std::map<std::string, std::set<std::string>> op_passing_outs_map = {
{"sgd", {"ParamOut", "MasterParamOut"}},
{"rmsprop",
{"ParamOut",
"MomentOut",
"MeanSquareOut",
"MeanGradOut",
"MasterParamOut"}},
{"ftrl", {"ParamOut", "SquaredAccumOut", "LinearAccumOut"}},
{"adadelta",
{"ParamOut",
"AvgSquaredGradOut",
"AvgSquaredUpdateOut",
"MasterParamOut"}},
{"adagrad", {"ParamOut", "MomentOut", "MasterParamOut"}},
{"adamax", {"ParamOut", "MomentOut", "InfNormOut", "MasterParamOut"}},
{"dpsgd", {"ParamOut"}},
{"decayed_adagrad", {"ParamOut", "MomentOut"}},
{"lars_momentum", {"ParamOut", "VelocityOut"}},
{"coalesce_tensor", {"Output", "FusedOutput"}},
{"adam",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Moment2MaxOut",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"merged_adam",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Moment2MaxOut",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"fused_adam",
{"ParamsOut",
"Moments1Out",
"Moments2Out",
"Moments2MaxOut",
"Beta1PowsOut",
"Beta2PowsOut",
"MasterParamsOut"}},
{"adamw",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Moment2MaxOut",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"lamb",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"average_accumulates",
{"out_sum_1",
"out_sum_2",
"out_sum_3",
"out_num_accumulates",
"out_old_num_accumulates",
"out_num_updates"}},
{"momentum", {"ParamOut", "VelocityOut", "MasterParamOut"}},
{"merged_momentum", {"ParamOut", "VelocityOut", "MasterParamOut"}},
{"sparse_momentum", {"ParamOut", "VelocityOut", "MasterParamOut"}},
{"batch_norm", {"MeanOut", "VarianceOut"}},
{"sync_batch_norm", {"MeanOut", "VarianceOut"}},
{"accuracy", {"Correct", "Total"}},
{"fill_constant", {"Out"}},
{"recv_v2", {"Out"}},
{"partial_recv", {"Out"}},
{"matmul", {"Out"}},
{"c_broadcast", {"Out"}},
{"c_sync_calc_stream", {"Out"}},
{"c_sync_comm_stream", {"Out"}},
{"c_reduce", {"Out"}},
{"c_scatter", {"Out"}},
{"barrier", {"Out"}},
{"fake_quantize_dequantize_moving_average_abs_max",
{"Out", "OutScale", "OutAccum", "OutState"}},
{"fake_quantize_dequantize_abs_max", {"Out", "OutScale"}},
{"fake_channel_wise_quantize_dequantize_abs_max", {"Out", "OutScale"}},
{"check_finite_and_unscale", {"Out", "FoundInfinite"}},
{"update_loss_scaling",
{"Out", "LossScaling", "OutGoodSteps", "OutBadSteps"}},
{"moving_average_abs_max_scale",
{"Out", "OutScale", "OutAccum", "OutState"}},
{"rnn", {"DropoutState"}},
{"run_program", {"Out", "DOut", "OutScope", "CUDAGraph"}},
{"clear_float_status", {"FloatStatusOut"}},
{"get_float_status", {"FloatStatusOut"}},
{"assign", {"Out"}},
{"assign_value", {"Out"}},
{"split", {"Out"}},
{"concat", {"Out"}},
{"fused_multi_transformer_int8", {"CacheKVOut"}},
{"group_norm", {"Mean", "Variance"}},
{"resnet_basic_block",
{"Mean1Out", "Var1Out", "Mean2Out", "Var2Out", "Mean3Out", "Var3Out"}},
};
std::map<std::string, std::set<std::string>> op_ins_map = {
{"fc", {"Input", "W", "Bias"}},
{"precision_recall",
{"MaxProbs", "Indices", "Labels", "Weights", "StatesInfo"}},
{"layer_norm", {"X", "Scale", "Bias"}},
{"fused_conv2d_add_act", {"Input", "Filter", "Bias", "ResidualData"}},
{"bincount", {"X", "Weights"}},
{"fused_attention",
{"X",
"LnScale",
"LnBias",
"QKVW",
"QKVBias",
"CacheKV",
"SrcMask",
"OutLinearW",
"OutLinearBias",
"Ln2Scale",
"Ln2Bias"}},
{"fused_gate_attention",
{"Query",
"Key",
"QueryWeight",
"KeyWeight",
"ValueWeight",
"QKVWeight",
"NonbatchedBias",
"SrcMask",
"GateWeight",
"GateBias",
"OutLinearWeight",
"OutLinearBias"}},
{"fused_multi_transformer_int8",
{"X", "LnScale", "LnBias", "QKVW",
"QKVBias", "CacheKV", "TimeStep", "SrcMask",
"OutLinearW", "OutLinearBias", "FFNLnScale", "FFNLnBias",
"FFN1Weight", "FFN1Bias", "FFN2Weight", "FFN2Bias",
"QKVOutScale", "OutLinearOutScale", "FFN1OutScale", "FFN2OutScale"}},
{"fused_bias_dropout_residual_layer_norm",
{"X", "Residual", "Bias", "LnScale", "LnBias"}},
{"instance_norm", {"X", "Scale", "Bias"}},
{"gru_unit", {"Input", "HiddenPrev", "Weight", "Bias"}},
{"label_smooth", {"X", "PriorDist"}},
{"assign", {"X"}},
{"crop", {"X", "Y", "Offsets"}},
{"crop_tensor", {"X", "Shape", "Offsets"}},
{"reshape2", {"X", "Shape"}},
{"expand", {"X", "ExpandTimes"}},
{"slice",
{"Input",
"StartsTensor",
"EndsTensor",
"StartsTensorList",
"EndsTensorList"}},
{"strided_slice",
{"Input",
"StartsTensor",
"EndsTensor",
"StridesTensor",
"StartsTensorList",
"EndsTensorList",
"StridesTensorList"}},
{"set_value",
{"Input",
"ValueTensor",
"StartsTensorList",
"EndsTensorList",
"StepsTensorList"}},
{"fake_quantize_dequantize_moving_average_abs_max",
{"X", "InScale", "InAccum", "InState"}},
{"nll_loss", {"X", "Label", "Weight"}},
{"bilinear_tensor_product", {"X", "Y", "Weight", "Bias"}},
{"gather", {"X", "Index", "Axis"}},
{"repeat_interleave", {"X", "RepeatsTensor"}},
{"roi_pool", {"X", "ROIs", "RoisNum"}},
{"roi_align", {"X", "ROIs", "RoisNum"}},
{"prroi_pool", {"X", "ROIs", "BatchRoINums"}},
{"psroi_pool", {"X", "ROIs", "RoisNum"}},
{"collect_fpn_proposals",
{"MultiLevelRois", "MultiLevelScores", "MultiLevelRoIsNum"}},
{"distribute_fpn_proposals", {"FpnRois", "RoisNum"}},
{"warpctc", {"Logits", "Label", "LogitsLength", "LabelLength"}},
{"hierarchical_sigmoid",
{"X", "W", "Label", "PathTable", "PathCode", "Bias"}},
{"moving_average_abs_max_scale", {"X", "InAccum", "InState"}},
{"multiclass_nms3", {"BBoxes", "Scores", "RoisNum"}},
{"box_coder", {"PriorBox", "PriorBoxVar", "TargetBox"}},
{"momentum", {"Param", "Grad", "Velocity", "LearningRate", "MasterParam"}},
{"merged_momentum",
{"Param", "Grad", "Velocity", "LearningRate", "MasterParam"}},
{"sparse_momentum",
{"Param", "Grad", "Velocity", "Index", "LearningRate", "MasterParam"}},
{"rnn", {"Input", "PreState", "WeightList", "SequenceLength"}},
{"run_program", {"X", "Params"}},
{"fused_feedforward",
{"Dropout1Seed",
"Dropout2Seed",
"Linear1Bias",
"Linear2Bias",
"Ln1Scale",
"Ln1Bias",
"Ln2Scale",
"Ln2Bias"}},
{"faster_tokenizer", {"Text", "Vocab", "TextPair"}},
{"matrix_rank", {"X", "TolTensor"}},
{"rmsprop",
{"Param",
"MeanSquare",
"Grad",
"Moment",
"LearningRate",
"MeanGrad",
"MasterParam"}},
{"adam",
{"Param",
"Grad",
"LearningRate",
"Moment1",
"Moment2",
"Moment2Max",
"Beta1Pow",
"Beta2Pow",
"MasterParam"}},
{"merged_adam",
{"Param",
"Grad",
"LearningRate",
"Moment1",
"Moment2",
"Moment2Max",
"Beta1Pow",
"Beta2Pow",
"MasterParam"}},
{"fused_adam",
{"Params",
"Grads",
"LearningRate",
"Moments1",
"Moments2",
"Moments2Max",
"Beta1Pows",
"Beta2Pows",
"MasterParams",
"SkipUpdate"}},
{"adamw",
{"Param",
"Grad",
"LearningRate",
"Moment1",
"Moment2",
"Moment2Max",
"Beta1Pow",
"Beta2Pow",
"MasterParam"}},
{"adamax",
{"Param",
"Grad",
"LearningRate",
"Moment",
"InfNorm",
"Beta1Pow",
"MasterParam"}},
{"lamb",
{"Param",
"Grad",
"LearningRate",
"Moment1",
"Moment2",
"Beta1Pow",
"Beta2Pow",
"MasterParam"}},
{"sparse_attention",
{"Q", "K", "V", "Offset", "Columns", "KeyPaddingMask", "AttnMask"}},
{"sgd", {"Param", "LearningRate", "Grad", "MasterParam"}},
{"adagrad", {"Param", "Grad", "Moment", "LearningRate", "MasterParam"}},
{"adadelta",
{"Param",
"Grad",
"AvgSquaredGrad",
"AvgSquaredUpdate",
"LearningRate",
"MasterParam"}},
{"graph_khop_sampler", {"Row", "Eids", "Col_Ptr", "X"}},
{"nce",
{"Input",
"Label",
"Weight",
"Bias",
"SampleWeight",
"CustomDistProbs",
"CustomDistAlias",
"CustomDistAliasProbs"}},
{"yolov3_loss", {"X", "GTBox", "GTLabel", "GTScore"}},
{"check_finite_and_unscale", {"X", "Scale", "FloatStatus"}},
{"group_norm", {"X", "Scale", "Bias"}},
{"linear_chain_crf", {"Emission", "Transition", "Label", "Length"}},
{"crf_decoding", {"Emission", "Transition", "Label", "Length"}},
{"chunk_eval", {"Inference", "Label", "SeqLength"}},
{"sequence_mask", {"X", "MaxLenTensor"}},
{"graph_reindex",
{"X", "Neighbors", "Count", "HashTable_Value", "HashTable_Index"}},
{"graph_sample_neighbors", {"Row", "Col_Ptr", "X", "Eids", "Perm_Buffer"}},
{"crop", {"X", "Y", "Offsets"}},
{"batch_norm",
{"X", "Scale", "Bias", "Mean", "Variance", "MomentumTensor"}},
{"linear_interp", {"X", "OutSize"}},
{"bilinear_interp", {"X", "OutSize"}},
{"trilinear_interp", {"X", "OutSize"}},
{"nearest_interp", {"X", "OutSize"}},
{"bicubic_interp", {"X", "OutSize"}},
{"resnet_basic_block",
{"X",
"Filter1",
"Scale1",
"Bias1",
"Mean1",
"Var1",
"Filter2",
"Scale2",
"Bias2",
"Mean2",
"Var2",
"Filter3",
"Scale3",
"Bias3",
"Mean3",
"Var3"}},
{"graph_send_recv", {"X", "Src_index", "Dst_index", "Out_size"}},
{"graph_send_ue_recv", {"X", "Y", "Src_index", "Dst_index", "Out_size"}},
};
std::map<std::string, std::set<std::string>> op_outs_map = {
{"rank_attention", {"InputHelp", "Out", "InsRank"}},
{"fake_quantize_dequantize_moving_average_abs_max",
{"Out", "OutScale", "OutAccum", "OutState"}},
{"batch_norm",
{"Y",
"MeanOut",
"VarianceOut",
"SavedMean",
"SavedVariance",
"ReserveSpace"}},
{"lstsq", {"Solution", "Residuals", "Rank", "SingularValues"}},
{"fused_attention", {"LnMean", "LnVariance",
"LnOut", "QKVOut",
"QKVBiasOut", "TransposeOut2",
"QKOut", "QKTVOut",
"SoftmaxOut", "AttnDropoutMaskOut",
"AttnDropoutOut", "SrcMaskOut",
"FMHAOut", "OutLinearOut",
"DropoutMaskOut", "Ln2Mean",
"Ln2Variance", "BiasDropoutResidualOut",
"CacheKVOut", "Y"}},
{"fused_bias_dropout_residual_layer_norm",
{"BiasDropoutResidualOut", "DropoutMaskOut", "LnMean", "LnVariance", "Y"}},
{"fused_gate_attention",
{"QueryTransposeOut",
"KeyTransposeOut",
"ValueTransposeOut",
"QKVTransposeOut",
"SoftmaxOut",
"FMHAOut",
"GateOut",
"Out"}},
{"sync_batch_norm",
{"Y",
"MeanOut",
"VarianceOut",
"SavedMean",
"SavedVariance",
"ReserveSpace"}},
{"adadelta",
{"ParamOut",
"AvgSquaredGradOut",
"AvgSquaredUpdateOut",
"MasterParamOut"}},
{"unique", {"Out", "Index", "Indices", "Counts"}},
{"unique_consecutive", {"Out", "Index", "Counts"}},
{"generate_proposals", {"RpnRois", "RpnRoiProbs", "RpnRoisNum"}},
{"collect_fpn_proposals", {"FpnRois", "RoisNum"}},
{"matrix_nms", {"Out", "Index", "RoisNum"}},
{"distribute_fpn_proposals",
{"MultiFpnRois", "RestoreIndex", "MultiLevelRoIsNum"}},
{"moving_average_abs_max_scale",
{"Out", "OutScale", "OutAccum", "OutState"}},
{"rmsprop",
{"ParamOut",
"MomentOut",
"MeanSquareOut",
"MeanGradOut",
"MasterParamOut"}},
{"multiclass_nms3", {"Out", "NmsRoisNum"}},
{"generate_proposals_v2", {"RpnRois", "RpnRoiProbs", "RpnRoisNum"}},
{"momentum", {"ParamOut", "VelocityOut", "MasterParamOut"}},
{"merged_momentum", {"ParamOut", "VelocityOut", "MasterParamOut"}},
{"sparse_momentum", {"ParamOut", "VelocityOut", "MasterParamOut"}},
{"rnn", {"DropoutState", "Reserve", "Out", "State"}},
{"run_program", {"DOut", "CUDAGraph"}},
{"adam",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Moment2MaxOut",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"merged_adam",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Moment2MaxOut",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"fused_adam",
{"ParamsOut",
"Moments1Out",
"Moments2Out",
"Moments2MaxOut",
"Beta1PowsOut",
"Beta2PowsOut",
"MasterParamsOut"}},
{"adamw",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Moment2MaxOut",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"adamax",
{"ParamOut", "MomentOut", "InfNormOut", "Beta1Pow", "MasterParamOut"}},
{"sgd", {"ParamOut", "MasterParamOut"}},
{"adagrad", {"ParamOut", "MomentOut", "MasterParamOut"}},
{"lamb",
{"ParamOut",
"Moment1Out",
"Moment2Out",
"Beta1PowOut",
"Beta2PowOut",
"MasterParamOut"}},
{"fused_multi_transformer_int8", {"CacheKVOut", "Out"}},
{"resnet_basic_block",
{"Y", "Conv1", "SavedMean1", "SavedInvstd1", "Mean1Out",
"Var1Out", "Conv2", "SavedMean2", "SavedInvstd2", "Mean2Out",
"Var2Out", "Conv3", "SavedMean3", "SavedInvstd3", "Mean3Out",
"Var3Out", "MaxInput1", "MaxFilter1", "MaxInput2", "MaxFilter2",
"MaxInput3", "MaxFilter3"}},
};
std::set<std::string> special_inplace_op_set = {
"sum", // `sum` op has duplicate input
"assign", // output of `assign` op is in `op_passing_outs_map`
};
std::set<std::string> special_no_need_buffer_op_set = {
"sequence_conv",
};
int run_generator(int argc, char* argv[]) {
std::string eager_root = argv[1];
int split_count = atoi(argv[2]);
paddle::framework::PrepareAttrMapForOps();
paddle::framework::DygraphCodeGeneration(eager_root, split_count);
return 0;
}