diff --git a/paddle/fluid/framework/details/CMakeLists.txt b/paddle/fluid/framework/details/CMakeLists.txt index 6fe8dcf6de989cf23e8cff7ba06585ba5c180c23..6621a59d37a670f7025507faeab5b9897794a72e 100644 --- a/paddle/fluid/framework/details/CMakeLists.txt +++ b/paddle/fluid/framework/details/CMakeLists.txt @@ -52,8 +52,7 @@ cc_library(fuse_vars_op_handle SRCS fuse_vars_op_handle.cc DEPS op_handle_base s cc_library(memory_optimize_helper SRCS memory_optimize_helper.cc DEPS graph graph_helper) cc_library(memory_optimize_pass SRCS memory_optimize_pass.cc DEPS memory_optimize_helper pass) -cc_library(graph_print_pass SRCS graph_print_pass.cc DEPS graph_helper pass) -cc_library(inplace_op_pass SRCS inplace_op_pass.cc DEPS memory_optimize_pass op_info graph_print_pass) +cc_library(inplace_op_pass SRCS inplace_op_pass.cc DEPS memory_optimize_pass op_info) cc_library(modify_op_lock_and_record_event_pass SRCS modify_op_lock_and_record_event_pass.cc DEPS computation_op_handle op_graph_view multi_devices_helper) cc_library(memory_early_delete_pass SRCS memory_early_delete_pass.cc DEPS memory_optimize_pass computation_op_handle scale_loss_grad_op_handle rpc_op_handle all_reduce_op_handle reduce_op_handle broadcast_op_handle data_balance_op_handle graph graph_helper pass) @@ -74,7 +73,6 @@ if (WITH_GPU) endif() cc_test(memory_optimize_helper_test SRCS memory_optimize_helper_test.cc memory_optimize_helper.cc DEPS framework_proto graph) cc_test(memory_optimize_pass_test SRCS memory_optimize_pass_test.cc memory_optimize_pass.cc memory_optimize_helper.cc DEPS framework_proto graph graph_helper op_registry pass) -cc_test(graph_print_pass_test SRCS graph_print_pass_test.cc DEPS graph_print_pass framework_proto graph graph_helper op_registry pass) cc_library(ssa_graph_executor SRCS ssa_graph_executor.cc DEPS ${SSA_GRAPH_EXECUTOR_DEPS}) @@ -99,4 +97,4 @@ cc_library(build_strategy SRCS build_strategy.cc DEPS multi_devices_graph_print_pass multi_devices_graph_check_pass fuse_elewise_add_act_pass multi_batch_merge_pass fuse_relu_depthwise_conv_pass - memory_optimize_pass lock_free_optimize_pass graph_print_pass) + memory_optimize_pass lock_free_optimize_pass) diff --git a/paddle/fluid/framework/details/build_strategy.cc b/paddle/fluid/framework/details/build_strategy.cc index 2cc40b7bcd8c59feb811d0b9204f323cab100d92..51ce9732722efa44d2489f5b77694094e58c8775 100644 --- a/paddle/fluid/framework/details/build_strategy.cc +++ b/paddle/fluid/framework/details/build_strategy.cc @@ -17,7 +17,6 @@ limitations under the License. */ #include #include -#include "paddle/fluid/framework/details/graph_print_pass.h" #include "paddle/fluid/framework/details/memory_optimize_helper.h" #include "paddle/fluid/framework/details/multi_devices_graph_pass.h" #include "paddle/fluid/framework/details/multi_devices_graph_print_pass.h" @@ -233,9 +232,6 @@ std::unique_ptr BuildStrategy::Apply( if (graph->Has(kAllOpDescs)) { graph->Erase(kAllOpDescs); } - if (!graph->Has(kGraphviz)) { - graph->Set(kGraphviz, new GraphvizNodes); - } graph->Set>( kAllOpDescs, new std::vector(main_program.Block(0).AllOps())); @@ -245,10 +241,6 @@ std::unique_ptr BuildStrategy::Apply( "GPU, skipped."; continue; } - } else if (pass->Type() == "graph_print_path") { - if (!graph->Has(kGraphviz)) { - graph->Set(kGraphviz, new GraphvizNodes); - } } graph = pass->Apply(std::move(graph)); } @@ -274,5 +266,4 @@ USE_PASS(all_reduce_deps_pass); USE_PASS(modify_op_lock_and_record_event_pass); USE_PASS(inplace_pass); USE_PASS(lock_free_optimize_pass); -USE_PASS(graph_print_pass); USE_PASS(graph_to_program_pass); diff --git a/paddle/fluid/framework/details/graph_print_pass.cc b/paddle/fluid/framework/details/graph_print_pass.cc deleted file mode 100644 index e024e993a789245385897791292191d5eb6e0ae8..0000000000000000000000000000000000000000 --- a/paddle/fluid/framework/details/graph_print_pass.cc +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "paddle/fluid/framework/details/graph_print_pass.h" -#include -#include -#include "paddle/fluid/framework/ir/graph_helper.h" - -namespace paddle { -namespace framework { -namespace details { - -class GraphvizVar : public GraphvizNode { - public: - GraphvizVar(ir::Node* n, const int& i) : GraphvizNode(n, i) {} - friend std::ostream& operator<<(std::ostream& sout, const GraphvizVar& var) { - sout << "var_" << var.id_ << " [label=\"" << var.node_->Name() << "\"]" - << std::endl; - return sout; - } -}; - -class GraphvizOp : public GraphvizNode { - public: - GraphvizOp(ir::Node* n, const int& i) : GraphvizNode(n, i) {} - friend std::ostream& operator<<(std::ostream& sout, const GraphvizOp& op) { - sout << "op_" + std::to_string(op.id_) << " [label=\"" << op.node_->Name() - << "\", shape=rect]" << std::endl; - sout << op.stream_.str(); - return sout; - } - template - void AddEdge(const Callback& cb) { - std::string op_name = "op_" + std::to_string(id_); - for (auto var : node_->inputs) { - std::string var_name = "var_" + std::to_string(cb(var)); - stream_ << var_name << "->" << op_name << std::endl; - } - for (auto var : node_->outputs) { - std::string var_name = "var_" + std::to_string(cb(var)); - stream_ << op_name << "->" << var_name << std::endl; - } - } - - template - void AddCustomEdge(const Callback& cb) { - stream_ << cb() << std::endl; - } - - private: - std::ostringstream stream_; -}; - -template -std::vector FilterByNodeWrapper(const Container& con) { - std::vector ret; - for (auto& node : con) { - auto i = dynamic_cast(node.get()); - if (i != nullptr) ret.emplace_back(i); - } - return ret; -} - -std::unordered_map SSAGraphPrinterImpl::ToGraphvizNode( - const ir::Graph& graph) const { - // Convert to GraphvizNode format - auto& graphviz_nodes = graph.Get(kGraphviz); - graphviz_nodes.clear(); - std::unordered_map vars; - std::unordered_map ops; - int var_id = 0; - int op_id = 0; - for (auto& node : graph.Nodes()) { - if (node->IsVar()) { - graphviz_nodes.emplace(new GraphvizVar(node, var_id)); - vars.emplace(std::make_pair(node, var_id++)); - } else if (node->IsOp()) { - std::unique_ptr op(new GraphvizOp(node, op_id++)); - ops[node] = op.get(); - graphviz_nodes.emplace(std::move(op)); - } else { - PADDLE_THROW("Unknown op type"); - } - } - - // Detect circle. Draw circle in different lines - std::vector> circles; - const std::string kCircleEdge = "[color=red,penwidth=3.0]"; - if (ir::FindCircleSubGraph(graph, &circles)) { - VLOG(3) << "Graph has circle! circles count : " << circles.size(); - for (auto& circle : circles) { - for (size_t i = 0; i < circle.size() - 1; ++i) { - GraphvizOp* prev = ops[circle[i]]; - GraphvizOp* next = ops[circle[i + 1]]; - std::string prev_op = "op_" + std::to_string(prev->Id()); - std::string next_op = "op_" + std::to_string(next->Id()); - prev->AddCustomEdge([&]() -> std::string { - return prev_op + "->" + next_op + kCircleEdge; - }); - } - } - } - return vars; -} - -void SSAGraphPrinterImpl::Print(const ir::Graph& graph, - std::ostream& sout) const { - auto vars = ToGraphvizNode(graph); - auto& nodes = graph.Get(kGraphviz); - - sout << "digraph G {\n"; - for (auto& var : FilterByNodeWrapper(nodes)) { - sout << *var; - } - - for (auto& op : FilterByNodeWrapper(nodes)) { - op->AddEdge([&vars](ir::Node* var) { return vars.at(var); }); - sout << *op; - } - sout << "}\n"; -} - -std::unique_ptr SSAGraphPrintPass::ApplyImpl( - std::unique_ptr graph) const { - printer_.reset(new SSAGraphPrinterImpl()); - std::unique_ptr fout( - new std::ofstream(Get(kGraphvizPath))); - PADDLE_ENFORCE(fout->good() == true, "Failed to open file."); - - printer_->Print(*graph, *fout); - return graph; -} - -} // namespace details -} // namespace framework -} // namespace paddle - -REGISTER_PASS(graph_print_pass, paddle::framework::details::SSAGraphPrintPass) - .RequirePassAttr(paddle::framework::details::kGraphvizPath); diff --git a/paddle/fluid/framework/details/graph_print_pass.h b/paddle/fluid/framework/details/graph_print_pass.h deleted file mode 100644 index ab506abbabb5c621ab21c71f26724c12f0f0d14f..0000000000000000000000000000000000000000 --- a/paddle/fluid/framework/details/graph_print_pass.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once -#include -#include -#include - -#include "paddle/fluid/framework/details/multi_devices_helper.h" - -namespace paddle { -namespace framework { -namespace details { - -constexpr char kGraphvizPath[] = "debug_graphviz_path"; -constexpr char kGraphviz[] = "graphviz"; - -// NOTE(dzhwinter): If the graph contains circles. -// the graph can not be topology sort. -// This printer will print the whole graph -// and highlight the circles. It's quite useful -// for debug the deadlock and circles. -class GraphvizNode { - public: - GraphvizNode(ir::Node* n, const int& i) : node_(n), id_(i) {} - virtual ~GraphvizNode() = default; - - int Id() const { return id_; } - - protected: - ir::Node* node_; - int id_; -}; - -typedef std::unordered_set> GraphvizNodes; - -class SSAGraphPrinter { - public: - virtual ~SSAGraphPrinter() {} - virtual void Print(const ir::Graph& graph, std::ostream& sout) const = 0; -}; - -class SSAGraphPrinterImpl : public SSAGraphPrinter { - public: - void Print(const ir::Graph& graph, std::ostream& sout) const override; - - private: - std::unordered_map ToGraphvizNode( - const ir::Graph& graph) const; -}; - -class SSAGraphPrintPass : public ir::Pass { - protected: - std::unique_ptr ApplyImpl( - std::unique_ptr graph) const override; - - private: - mutable std::unique_ptr printer_; -}; -} // namespace details -} // namespace framework -} // namespace paddle diff --git a/paddle/fluid/framework/details/graph_print_pass_test.cc b/paddle/fluid/framework/details/graph_print_pass_test.cc deleted file mode 100644 index d8fd1beba38d64c3c1f14d13122b503c8b4f657f..0000000000000000000000000000000000000000 --- a/paddle/fluid/framework/details/graph_print_pass_test.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "paddle/fluid/framework/details/graph_print_pass.h" -#include "paddle/fluid/framework/details/graph_test_base.h" - -REGISTER_OPERATOR(sum, paddle::framework::DummyOp, - paddle::framework::SumOpMaker); -REGISTER_OPERATOR(split, paddle::framework::DummyOp, - paddle::framework::SplitOpMaker); -REGISTER_OPERATOR(assign, paddle::framework::DummyOp, - paddle::framework::AssignOpMaker, - paddle::framework::DummyVarTypeInference); - -/* - a @ b - c - d @ e - */ - -using paddle::framework::ProgramDesc; -using paddle::framework::proto::VarType; - -inline static ProgramDesc FillProgramDesc() { - ProgramDesc prog; - prog.MutableBlock(0)->Var("a")->SetType(VarType::LOD_TENSOR); - prog.MutableBlock(0)->Var("b")->SetType(VarType::LOD_TENSOR); - prog.MutableBlock(0)->Var("c")->SetType(VarType::LOD_TENSOR); - prog.MutableBlock(0)->Var("d")->SetType(VarType::LOD_TENSOR); - prog.MutableBlock(0)->Var("e")->SetType(VarType::LOD_TENSOR); - { - auto* op = prog.MutableBlock(0)->AppendOp(); - op->SetType("sum"); - op->SetInput("X", {"a", "b"}); - op->SetOutput("Out", {"c"}); - } - { - auto* op = prog.MutableBlock(0)->AppendOp(); - op->SetType("split"); - op->SetInput("X", {"c"}); - op->SetOutput("Out", {"d", "e"}); - } - { - auto* op = prog.MutableBlock(0)->AppendOp(); - op->SetType("sum"); - op->SetInput("X", {"d", "e"}); - op->SetOutput("Out", {"d"}); - } - { - auto* op = prog.MutableBlock(0)->AppendOp(); - op->SetType("assign"); - op->SetInput("X", {"d"}); - op->SetOutput("Out", {"d"}); - } - return prog; -} - -namespace paddle { -namespace framework { -namespace details { - -TEST(SSAGraphPrinter, Normal) { - auto program = FillProgramDesc(); - std::unique_ptr graph(new ir::Graph(program)); - graph->Set(kGraphviz, new GraphvizNodes); - std::unique_ptr printer(new SSAGraphPrinterImpl); - - // redirect debug graph to a file. - constexpr char graph_path[] = "graph_print_pass.txt"; - std::unique_ptr fout(new std::ofstream(graph_path)); - PADDLE_ENFORCE(fout->good()); - printer->Print(*graph, *fout); -} - -using ir::Graph; -using ir::Node; -void BuildCircleGraph(Graph* g) { - ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation); - ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable); - - o1->outputs.push_back(v1); - o1->inputs.push_back(v1); - v1->inputs.push_back(o1); - v1->outputs.push_back(o1); -} - -void BuildCircleGraph2(Graph* g) { - ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation); - ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation); - ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable); - ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable); - - o1->outputs.push_back(v1); - o2->inputs.push_back(v1); - v1->inputs.push_back(o1); - v1->outputs.push_back(o2); - - o2->outputs.push_back(v2); - o1->inputs.push_back(v2); - v2->inputs.push_back(o2); - v2->outputs.push_back(o1); -} - -void BuildNoCircleGraph(Graph* g) { - ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation); - ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation); - ir::Node* o3 = g->CreateEmptyNode("op3", Node::Type::kOperation); - ir::Node* o4 = g->CreateEmptyNode("op4", Node::Type::kOperation); - ir::Node* o5 = g->CreateEmptyNode("op5", Node::Type::kOperation); - ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable); - ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable); - ir::Node* v3 = g->CreateEmptyNode("var3", Node::Type::kVariable); - ir::Node* v4 = g->CreateEmptyNode("var4", Node::Type::kVariable); - - // o1->v1->o2 - o1->outputs.push_back(v1); - o2->inputs.push_back(v1); - v1->inputs.push_back(o1); - v1->outputs.push_back(o2); - // o2->v2->o3 - // o2->v2->o4 - o2->outputs.push_back(v2); - o3->inputs.push_back(v2); - o4->inputs.push_back(v2); - v2->inputs.push_back(o2); - v2->outputs.push_back(o3); - v2->outputs.push_back(o4); - // o2->v3->o5 - o2->outputs.push_back(v3); - o5->inputs.push_back(v3); - v3->inputs.push_back(o2); - v3->outputs.push_back(o5); - // o3-v4->o5 - o3->outputs.push_back(v4); - o5->inputs.push_back(v4); - v4->inputs.push_back(o3); - v4->outputs.push_back(o5); - - // o2->v3->o1 - v3->outputs.push_back(o1); - o1->inputs.push_back(v3); -} - -TEST(SSAGraphPrinter, SimpleCircle) { - ProgramDesc prog; - - Graph graph(prog); - BuildCircleGraph(&graph); - ASSERT_TRUE(HasCircle(graph)); - - graph.Set(kGraphviz, new GraphvizNodes); - std::unique_ptr printer(new SSAGraphPrinterImpl); - - // redirect debug graph to a file. - constexpr char graph_path[] = "graph_print_pass_simple_circle.txt"; - std::unique_ptr fout(new std::ofstream(graph_path)); - PADDLE_ENFORCE(fout->good()); - printer->Print(graph, *fout); -} - -TEST(SSAGraphPrinter, ComplexCircle) { - ProgramDesc prog; - Graph graph(prog); - BuildCircleGraph2(&graph); - ASSERT_TRUE(HasCircle(graph)); - - graph.Set(kGraphviz, new GraphvizNodes); - std::unique_ptr printer(new SSAGraphPrinterImpl); - - // redirect debug graph to a file. - constexpr char graph_path[] = "graph_print_pass_complex_circle.txt"; - std::unique_ptr fout(new std::ofstream(graph_path)); - PADDLE_ENFORCE(fout->good()); - printer->Print(graph, *fout); -} - -} // namespace details -} // namespace framework -} // namespace paddle diff --git a/paddle/fluid/framework/details/inplace_op_pass.cc b/paddle/fluid/framework/details/inplace_op_pass.cc index 13ae02a6f3f9ea9f8faa9afb6c2adc319b37c260..ff3aacfe1001650df94684f47d4d256833227dc9 100644 --- a/paddle/fluid/framework/details/inplace_op_pass.cc +++ b/paddle/fluid/framework/details/inplace_op_pass.cc @@ -21,7 +21,6 @@ #include #include #include -#include "paddle/fluid/framework/details/graph_print_pass.h" #include "paddle/fluid/framework/details/memory_optimize_pass.h" #include "paddle/fluid/framework/ir/graph_helper.h" #include "paddle/fluid/framework/op_info.h" @@ -114,24 +113,6 @@ static inline ir::Node* GetPrevCascadeInplacedVar(ir::Node* var) { return input_it == prev_op->inputs.end() ? nullptr : *input_it; } -template -static inline bool ConnectByCtrlVar(const Container& group1, - const Container& group2) { - bool connected = false; - std::unordered_set outputs; - for (auto* op : group1) { - for (auto* var : op->outputs) { - if (var->IsCtrlVar()) outputs.emplace(var); - } - } - for (auto* op : group2) { - for (auto* var : op->inputs) { - if (outputs.count(var)) connected = true; - } - } - return connected; -} - InplacePass::InplacePass() : Pass() { if (FLAGS_enable_inplace_whitelist) { for (auto& s : kInplacedOpWhiteList) { @@ -316,18 +297,7 @@ void InplacePass::TryInplaceOpInputOutput(ir::Node* op, continue; } - // 3. if output reuse input inplaced, the dependency group is not changed. - // For detail, check - // the function description in "OutConnectInputByCtrlVar" - if (view_.OutConnectInputByCtrlVar(in_node, out_node)) { - VLOG(4) << string::Sprintf( - "Skiped pair %s => %s. %s input and output connect by ctrl var." - "inplace such pair will generate a circle.", - out_var_name, in_var_name, op->Name()); - continue; - } - - // 4. if output has been memory optimize by python(fluid.memory_optmize()). + // 3. if output has been memory optimize by python(fluid.memory_optmize()). // this candidate can not be inplaced. Will be deprecated in the future. if (view_.ReusedInPythonMemOpt(out_node->Name())) { VLOG(4) << string::Sprintf( @@ -431,48 +401,6 @@ void GraphView::Build(ir::Graph* g) { const std::vector GraphView::AllOps() { return ops_; } -bool GraphView::OutConnectInputByCtrlVar(ir::Node* in_var, ir::Node* out_var) { - // assume v_a0, v_a1 is variable. v_a0 -> v_a0 means already inplaced. - // v_a1 -> v_a1 means already inplaced. - // Currently we make decision to check if the v_a0 -> v_a1 can be inplace. - // - // v_a0 - // + - // | - // v - // v_a0 - // + - // | - // v - // v_a1 - // + - // | - // v - // v_a1 - // start from the first inplaced input v_a0(on the top one). - // Do a DFSSearch, get all its paths. If there is one path connect - // the in_var and out_var which contains control dep var. - // Means there a control path. out_var can not be inplaced use in_var. - - std::unordered_set out_var_set, in_var_set; - ir::Node* out = out_var; - // get the ops with same output name - while (out != nullptr) { - out_var_set.emplace(out); - out = GetNextCascadeInplacedVar(out); - } - - // get ops with same input name - ir::Node* in = in_var; - while (in != nullptr) { - in_var_set.emplace(in); - in = GetPrevCascadeInplacedVar(in); - } - // find if there is path with control dep var connect the in_var_set and - // out_var_set - return ConnectByCtrlVar(in_var_set, out_var_set); -} - bool GraphView::ReusedInPythonMemOpt(const std::string& var) const { return dup_nodes_.count(var); } diff --git a/paddle/fluid/framework/details/inplace_op_pass.h b/paddle/fluid/framework/details/inplace_op_pass.h index 203ffe6e24486080116293dfeb310432df5c000d..255b3b8e83395967f61bdfbb99c948265f925847 100644 --- a/paddle/fluid/framework/details/inplace_op_pass.h +++ b/paddle/fluid/framework/details/inplace_op_pass.h @@ -40,8 +40,6 @@ class GraphView { std::vector PendingOpsOnVar(ir::Node* var); - bool OutConnectInputByCtrlVar(ir::Node* in_var, ir::Node* out_var); - // Will Deperated in the future. // NOTE(dzhwinter) : Python memory optimize will reuse // memory based var name, so different op output may diff --git a/paddle/fluid/framework/details/multi_devices_graph_print_pass.h b/paddle/fluid/framework/details/multi_devices_graph_print_pass.h index 69cac8ad95400e061fe0b52b6e10dc90e79a85d0..b06c87a5c185c550818af0bdeacd0070d1d90e4e 100644 --- a/paddle/fluid/framework/details/multi_devices_graph_print_pass.h +++ b/paddle/fluid/framework/details/multi_devices_graph_print_pass.h @@ -19,12 +19,20 @@ #include #include #include -#include "paddle/fluid/framework/details/graph_print_pass.h" +#include "paddle/fluid/framework/details/multi_devices_helper.h" namespace paddle { namespace framework { namespace details { +constexpr char kGraphvizPath[] = "debug_graphviz_path"; + +class SSAGraphPrinter { + public: + virtual ~SSAGraphPrinter() {} + virtual void Print(const ir::Graph& graph, std::ostream& sout) const = 0; +}; + class GraphvizSSAGraphPrinter : public SSAGraphPrinter { public: void Print(const ir::Graph& graph, std::ostream& sout) const override;