Skip to content

Commit

Permalink
add piano graph executor (PaddlePaddle#36)
Browse files Browse the repository at this point in the history
* add piano graph executor

* change VarType_Type to VarType::Type

* add single test script

* complete some test script

* add enforce at convert vartype

* fix some bug and merge meta op PR change

* fix VarType2NoteType bug

* improve and perfect test script

* use vector instead of unordered_set

* perfect topo sort by @wzzju 's review advise

* fix get var data type problem

* fix print string not c-string bug

* perfect PianoGraphExecutor class structure by @wzzju 's review advise

* optimize vartype utils by wzzju 's advice

* fix some potential problem
  • Loading branch information
thisjiang authored Sep 2, 2021
1 parent 023f984 commit c29ab78
Show file tree
Hide file tree
Showing 8 changed files with 640 additions and 2 deletions.
5 changes: 5 additions & 0 deletions paddle/fluid/compiler/paddle2piano/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ cc_test(piano_op_registry_test SRCS piano_op_registry_test.cc DEPS piano_op_regi

cc_library(piano_op_kernel_context SRCS piano_op_kernel_context.cc DEPS piano_op_registry proto_desc piano_symbolization_builder)
cc_test(piano_op_kernel_context_test SRCS piano_op_kernel_context_test.cc DEPS piano_op_kernel_context op_registry)

cc_library(vartype_utils SRCS vartype_utils.cc DEPS proto_desc data_type note_proto)

cc_library(piano_graph_executor SRCS piano_graph_executor.cc DEPS piano_op_kernel_context piano_symbolization_meat_op vartype_utils)
cc_test(piano_graph_executor_test SRCS piano_graph_executor_test.cc DEPS piano_graph_executor node vartype_utils)
155 changes: 155 additions & 0 deletions paddle/fluid/compiler/paddle2piano/piano_graph_executor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/* 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 "paddle/fluid/compiler/paddle2piano/piano_graph_executor.h"

#include <memory>
#include <queue>
#include <unordered_map>
#include <unordered_set>

#include "paddle/fluid/compiler/paddle2piano/piano_op_kernel_context.h"
#include "paddle/fluid/compiler/paddle2piano/vartype_utils.h"
#include "paddle/fluid/compiler/piano/symbolization/meta_op.h"
#include "paddle/fluid/platform/enforce.h"

namespace paddle {
namespace piano {

using framework::ir::Node;
using GraphNodeVec = PianoGraphExecutor::GraphNodeVec;

std::unique_ptr<PianoScope> PianoGraphExecutor::CreateInputOperand(
symbolization::NoteBuilder* builder) const {
std::unique_ptr<PianoScope> scope = std::make_unique<PianoScope>();
for (int64_t id = 0; id < cluster_inputs_.size(); ++id) {
auto* node = cluster_inputs_.at(id);
PADDLE_ENFORCE_EQ(node->IsVar(), true,
platform::errors::InvalidArgument(
"Cluster Sub-Graph Input should be var"));

const auto& var_name = node->Name();

// create operand shape
const auto& var_shape = node->Var()->GetShape();
const auto& var_type = utils::GetVarDataType(node->Var());

// convert framework vartype to piano note type
note::ElementTypeProto element_type = utils::VarType2NoteType(var_type);
Shape operand_shape(element_type, var_shape);

// create Operand
symbolization::Operand op =
symbolization::Parameter(builder, id, operand_shape, var_name);

// store into PianoScope
scope->SetOperand(var_name, op);
}
return scope;
}

GraphNodeVec PianoGraphExecutor::SortInternalCluster() const {
GraphNodeVec cluster_sorted;
std::unordered_set<Node*> cluster_set(cluster_.cbegin(), cluster_.cend());

std::unordered_map<Node*, size_t> indegree;
std::unordered_map<Node*, std::unordered_map<Node*, size_t>> adj_list;
std::queue<Node*> topo_queue;

// record all op's input op and output op
for (auto* n : cluster_) {
PADDLE_ENFORCE_EQ(n->IsOp(), true,
platform::errors::PreconditionNotMet(
"Cluster's node all should be op node"));
PADDLE_ENFORCE_EQ(PianoOpRegistry::IsPianoOp(n->Name()), true,
platform::errors::PreconditionNotMet(
"Cluster's op all should be piano op"));
// the op's input is var
for (auto* in_var : n->inputs) {
// the var's input is op
for (auto* in_op : in_var->inputs) {
if (cluster_set.find(in_op) != cluster_set.end()) {
++indegree[n];
++adj_list[in_op][n];
}
}
}
}

// find topology entrance
for (auto* n : cluster_) {
if (indegree[n] == 0) {
topo_queue.push(n);
}
}

// topological sorting
while (!topo_queue.empty()) {
auto* cur_op = topo_queue.front();
topo_queue.pop();

cluster_sorted.emplace_back(cur_op);
for (const auto& adj_pair : adj_list[cur_op]) {
// decrease output op's in-degree
indegree.at(adj_pair.first) -= adj_pair.second;

// if empty, push into queue
if (indegree.at(adj_pair.first) == 0) {
topo_queue.push(adj_pair.first);
}
}
}

PADDLE_ENFORCE_EQ(cluster_sorted.size(), cluster_.size(),
platform::errors::PreconditionNotMet(
"Cluster Sub-Graph shouldn't contain cycle."));
return cluster_sorted;
}

void PianoGraphExecutor::RunCompile(const GraphNodeVec& cluster,
PianoScope* scope,
symbolization::NoteBuilder* builder) const {
for (auto* n : cluster) {
const auto& op_name = n->Name();
const auto* op_desc = n->Op();

const auto& op_kernel_map = PianoOpRegistry::AllPianoOpKernels(op_name);
// TODO(jiangcheng05): how to distinguish library's kernel, like cudnn?
op_kernel_map.at("PLAIN")(PianoOpKernelContext(op_desc, scope, builder));
}
}

note::ModuleProto PianoGraphExecutor::operator()() const {
// Step1: create unique NoteBuilder
std::string builder_name = "NoteBuilderOfGraph_";
builder_name.append(std::to_string(graph_id_));

symbolization::NoteBuilder builder(builder_name);

// Step2: create graph's input operand
auto scope = CreateInputOperand(&builder);

// Step3: topo sort graph
// rvalue references avoid useless copy
const auto& cluster_sorted = SortInternalCluster();

// Step4: get PianoOpKernel and run compile
RunCompile(cluster_sorted, scope.get(), &builder);

// Step5: build and return module
return builder.Build();
}

} // namespace piano
} // namespace paddle
95 changes: 95 additions & 0 deletions paddle/fluid/compiler/paddle2piano/piano_graph_executor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* 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. */

#pragma once

#include <vector>

#include "paddle/fluid/compiler/paddle2piano/piano_scope.h"
#include "paddle/fluid/compiler/piano/note/note.pb.h"
#include "paddle/fluid/compiler/piano/symbolization/note_builder.h"
#include "paddle/fluid/framework/ir/node.h"

namespace paddle {
namespace piano {

// An executor accept sub-graph which is generated by PianoCompilePass,
// run each op's PianoOpKernel, finally return the graph's ModuleProto.
//
// Parameter:
// 1. graph_id: the unique graph id, used for generating unique notebuilder name
// 2. cluster: a vector which contains all graph op, non-topological-sorting.
// 3. cluster_inputs: a vector which contains all graph's input var, the var's
// input are outside op, the output are inside op
// 4. cluster_outputs: a vector which contains all graph's output var, the var's
// input are inside op, the output are outside op
// 5. cluster_internals: a vector which contains all graph's internal var, the
// var's input and output are inside op
//
// Example:
// -------------------------> op3 -> var4 ->
// / /
// -> var1 -> op1 -> var2 -> op2 -> var3
//
// cluster: [op1, op2, op3]
// cluster_inputs: [var1]
// cluster_outputs: [var4]
// cluster_internals: [var2, var3]
//
// Describe:
// The executor consisted by the following step:
// 1. create a NoteBuilder, it's name is unique for each graph
// 2. create PianoScope, initially, scope only consist graph's input var and its
// operand
// 3. topological sorting graph
// 4. create PianoOpKernelContext and run each op's PianoOpKernel
// 5. run NoteBuilder's Build function to generate graph's ModuleProto
class PianoGraphExecutor {
public:
using GraphNodeVec = std::vector<framework::ir::Node*>;

PianoGraphExecutor(int64_t graph_id, const GraphNodeVec& cluster,
const GraphNodeVec& cluster_inputs,
const GraphNodeVec& cluster_outputs,
const GraphNodeVec& cluster_internals)
: graph_id_(graph_id),
cluster_(cluster),
cluster_inputs_(cluster_inputs),
cluster_outputs_(cluster_outputs),
cluster_internals_(cluster_internals) {}

note::ModuleProto operator()() const;

private:
const int64_t graph_id_;
const GraphNodeVec& cluster_;
const GraphNodeVec& cluster_inputs_;
const GraphNodeVec& cluster_outputs_;
const GraphNodeVec& cluster_internals_;

// create graph's input operand from cluster_inputs_
// why return std::unique_ptr ? PianoScope DISABLE_COPY_AND_ASSIGN
std::unique_ptr<PianoScope> CreateInputOperand(
symbolization::NoteBuilder* builder) const;

// run PianoOpKernel's Compile
void RunCompile(const GraphNodeVec& cluster, PianoScope* scope,
symbolization::NoteBuilder* builder) const;

// topologic sorting graph node
GraphNodeVec SortInternalCluster() const;
};

} // namespace piano
} // namespace paddle
Loading

0 comments on commit c29ab78

Please sign in to comment.