Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add piano graph executor #36

Merged
merged 17 commits into from
Sep 2, 2021

Conversation

thisjiang
Copy link
Collaborator

PR types

New features

PR changes

Others

Describe

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. pcluster_internals: a vector which contains all graph's internal var, the var's input and output are inside op

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

}

template <framework::proto::VarType::Type T>
constexpr note::ElementTypeProto VarType2NoteType();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个模板函数有在哪里用到吗

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

暂时并没有用到。。。

namespace paddle {
namespace piano {

TEST(GraphExecutorTest, basic) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

符号执行的子步骤单测需要测下吗

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这些子步骤都写在了.cc文件里,单独拎出来单测有点麻烦。。。目前拓扑排序和RunCompile我是通过Compile函数里往GetCompileOrder添加op_type来测试的,这方面可以完善下。CreateInputOperand是通过ModuleProto的值来判断,这个了解不多,应该也能完善

Comment on lines 73 to 74
in_ops.emplace(n, std::unordered_set<Node*>());
out_ops.emplace(n, std::unordered_set<Node*>());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么拓扑排序要写的如此麻烦,不能使用一个std::vector<Node*, size_t>维护入度吗?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不能啊。由于op Nodeinputsvar Node,而不同var Nodeinputs可能会有重叠,如果用std::vector<Node*, size_t>的话,这重叠的op Node就被重复计算,但后面topo遍历时却只会减一次。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

形象点说就是这样:

op1   op2
|   /   |
var1    var2
 \    /
  op3

如果用vector,计算op3的入度时,op2会被统计两次。但topo遍历时,op2只遍历了一次,op3的入度也就只减了一次。

Copy link
Owner

@wzzju wzzju Aug 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我的理解,即使你说的这种case,下面这种写法也是正确的。

void TopologicSortGraph(const GraphNodeVec& cluster,
                        GraphNodeVec* cluster_sorted) {

  std::unordered_set<Node*> cluster_set(cluster.cbegin(), cluster.cend());
  std::unordered_map<Node*, std::vector<Node*>> adj_list;
  std::unordered_map<Node*, size_t> in_degree;
  std::queue<Node*> queue;

  for (auto* n : cluster) {
    PADDLE_ENFORCE_EQ(n->IsOp(), true,
                  platform::errors::InvalidArgument(
                      "Cluster Sub-Graph all should be 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.count(in_op) != 0) {
          adj_list[in_op].emplace_back(n);
          in_degree[n]++;
        }
      }
    }
  }

  // find topology entries
  for (auto* n : cluster) {
    if (!in_degree[n]) {
      queue.push(n);
    }
  }

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

    cluster_sorted->emplace_back(cur_op);
    for (auto* adj : adj_list[cur_op]) {
      in_degree[adj]--;

      if (!in_degree[adj]) {
        queue.push(adj);
      }
    }
  }

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

}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样写的确可以哈,就是adj_list会有重复node。但在重复结点数目不多的情况下,相比unordered_set耗时会短点,我改下

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::unordered_map<Node*, std::unordered_set<Node*>> out_ops;替换为std::unordered_map<Node*, size_t>多数情况下也能减少空间吧,另外,重复node出现的概率不会太高且重复的次数一般也较少。这样的代码可读性个人感觉好一些。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修改~

Copy link
Owner

@wzzju wzzju Aug 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果想去重的话,可以试试下面的写法:

void TopologicSortGraph(const GraphNodeVec& cluster,
                        GraphNodeVec* cluster_sorted) {

  std::unordered_set<Node*> cluster_set(cluster.cbegin(), cluster.cend());
  std::unordered_map<Node*, std::unordered_map<Node*, size_t>> adj_list;
  std::unordered_map<Node*, size_t> indegree;
  std::queue<Node*> queue;

  for (auto* n : cluster) {
    PADDLE_ENFORCE_EQ(n->IsOp(), true,
                  platform::errors::InvalidArgument(
                      "Cluster Sub-Graph all should be 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.count(in_op) != 0) {
          ++adj_list[in_op][n];
          ++indegree[n];
        }
      }
    }
  }

  // find topology entries
  for (auto* n : cluster) {
    if (!indegree[n]) {
      queue.push(n);
    }
  }

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

    cluster_sorted->emplace_back(cur_op);
    for(auto it = adj_list[cur_op].begin(); it != adj_list[cur_op].end(); it++){
      indegree[it->first] -= it->second;

      if (!indegree[it->first]) {
        queue.push(it->first);
      }
    }
  }

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

}

Comment on lines 73 to 74
in_ops.emplace(n, 0);
out_ops.emplace(n, std::vector<Node*>());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这些赋值其实并没有什么必要。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in_ops.emplace(n, 0);这个有必要吧?unordered_map对于内置类型不会赋初始值0吧?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 63 to 64
std::unordered_map<Node*, size_t> in_ops;
std::unordered_map<Node*, std::vector<Node*>> out_ops;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议使用indegreeadj_list命名,便于理解。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的,我改下

wzzju
wzzju previously approved these changes Sep 1, 2021
Copy link
Owner

@wzzju wzzju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

CtfGo
CtfGo previously approved these changes Sep 1, 2021
Copy link
Collaborator

@CtfGo CtfGo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


static const std::unordered_map<framework::proto::VarType::Type,
note::ElementTypeProto>&
GetVarType2NoteTypeMap() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

头文件中定义static 函数并不建议,而且你的map也是static的。如果被多个源文件包含,那么每个源文件会有一份独立的函数和变量。如果多个.cc文件引用这个头文件,会导致link变慢。不建议这么写。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done,已修改为extern形式

return vartype2notetype;
}

static note::ElementTypeProto VarType2NoteType(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 53 to 61
note::ElementTypeProto VarType2NoteType(framework::proto::VarType::Type type) {
const auto& vartype2notetype = GetVarType2NoteTypeMap();
PADDLE_ENFORCE_NE(vartype2notetype.find(type), vartype2notetype.end(),
"Unsupported value data type (%s)",
framework::DataTypeToString(type).c_str());
return vartype2notetype.at(type);
}

VarType::Type PianoGraphExecutor::GetVarDataType(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉VarType2NoteTypeGetVarDataType的定义可以拿出来放在vartype_utils.cc中,并将vartype2notetype.h更改为vartype_utils.h,让后将VarType2NoteTypeGetVarDataType的声明放在vartype_utils.h。GetVarDataType放在类中作为static函数,但是又感觉与PianoGraphExecutor无关。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// Step2: create graph's input operand
PianoScope scope;
CreateInputOperand(&scope, &builder);
Copy link
Owner

@wzzju wzzju Sep 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1)将CreateInputOperand声明为PianoScope CreateInputOperand(symbolization::NoteBuilder* builder),scope作为返回值。
2)TopologicSortCluster改名为SortInternalCluster并将签名换为GraphNodeVec SortInternalCluster()
以上两点建议是不是更好呢?

详见:https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/classes/
29b2f3b069f080b8dd34b41c59d114c8

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PianoScopeDISABLE_COPY_AND_ASSIGN的,所以不能直接返回,改为返回std::unique_ptr<PianoScope>。另外SortInternalCluster改为返回GraphNodeVec并在调用处通过auto&&避免拷贝。

symbolization::NoteBuilder builder(builder_name);

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这是在想通过右值引用延长变量的生命周期吗?其实个人感觉 auto scope = CreateInputOperand(&builder);即可。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个写成auto cluster_sorted = SortInternalCluster();也是几乎没有开销的,调用的是vector的移动构造函数。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// store into PianoScope
scope->SetOperand(var_name, op);
}
return std::move(scope);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不需要写move,直接return scope;即可。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Owner

@wzzju wzzju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Copy link
Collaborator

@CtfGo CtfGo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@CtfGo CtfGo merged commit c29ab78 into wzzju:paddle_compiler Sep 2, 2021
wzzju pushed a commit that referenced this pull request Oct 19, 2021
* 1. add interface for fft;
2. add data type predicate;
3. fix paddle.roll.

* add fft c2c cufft kernel

* implement argument checking & op calling parts for fft_c2c and fftn_c2c

* add operator and opmaker definitions

* only register float and double for cpu.

* add common code for implementing FFT, add pocketfft as a dependency

* add fft c2c cufft kernel function

* fix bugs in python interface

* add support for c2r, r2c operators, op makers, kernels and kernel functors.

* test and fix bugs

* 1. fft_c2c function: add support for onesided=False;
2. add complex<float>, complex<double> support for concat and flip.

* 1. fft: fix python api bugs;
2. shape_op: add support for complex data types.

* fft c2c cufft kernel done with complie and link

* fix shape_op, add mkl placeholder

* remove mkl

* complete fft c2c in gpu

* 1. implement mkl-based fft, FFTC2CFunctor and common function exec_fft;
2. change the design, add input and output typename as template parameter for all FFTFunctors, update pocketfft-based implementation.

* complete fft c2c on gpu in ND

* complete fft c2c on gpu in ND

* complete fft c2c backward in ND

* fix MKL-based implementation

* Add frame op and CPU/GPU kernels.

* Add frame op forward unittest.

* Add frame op forward unittest.

* Remove axis parameter in FrameFunctor.

* Add frame op grad CPU/GPU kernels and unittest.

* Add frame op grad CPU/GPU kernels and unittest.

* Update doc string.

* Update after review and remove librosa requirement in unittest.

* Update grad kernel.

* add fft_c2r op

* Remove data allocation in TransCompute function.

* add fft r2c onesided with cpu(pocketfft/mkl) and gpu

* last fft c2r functor

* fix C2R and R2C for cufft, becase the direction is not an option in these cases.

* add fft r2c onesided with cpu(pocketfft/mkl) and gpu

* fix bugs in python APIs

* fix fft_c2r grad kernal

* fix bugs in python APIs

* add cuda fft c2r grad kernal functor

* clean code

* fix fft_c2r python API

* fill fft r2c result with conjugate symmetry (#19)

fill fft r2c result with conjugate symmetry

* add placeholder for unittests (#24)

* simple parameterize test function by auto generate test case from parm list (#25)

* miscellaneous fixes for python APIs (#26)

* add placeholder for unittests

* resize fft inputs before computation is n or s is provided.

* add complex kernels for pad and pad_grad

* simplify argument checking.

* add type promotion

* add int to float or complex promotion

* fix output data type for static mode

* fix fft's input dtype dispatch, import fft to paddle

* fix typos in axes checking (#27)

* fix typos in axes checking

* fix argument checking (#28)

* fix argument checking

* Add C2R Python layer normal and abnormal use cases (#29)

* documents and single case

* test c2r case

* New C2R Python layer normal and exception use cases

* complete rfft,rfft2,rfftn,ihfft,ihfft2,ihfftn unittest and doc string (#30)

* Documentation of the common interfaces of c2r and c2c (#31)

* Documentation of the common interfaces of c2r and c2c

* clean c++ code  (#32)

* clean code

* Add numpy-based implementation of spectral ops (#33)

* add numpy reference implementation of spectral ops

* Add fft_c2r numpy based implementation for unittest. (#34)

* add fft_c2r numpy implementation

* Add deframe op and stft/istft api. (#23)

* Add frame api

* Add deframe op and kernels.

* Add stft and istft apis.

* Add deframe api. Update stft and istft apis.

* Fix bug in frame_from_librosa function when input dims >= 3

* Rename deframe to overlap_add.

* Update istft.

* Update after code review.

* Add overlap_add op and stft/istft api unittest (#35)

* Add overlap_add op unittest.

* Register complex kernels of squeeze/unsquuze op.

* Add stft/istft api unittest.

* Add unittest for fft helper functions (#36)

* add unittests for fft helper functions. add complex kernel for roll op.

* complete static graph unittest for all public api (#37)

* Unittest of op with FFT C2C, C2R and r2c added (#38)

* documents and single case

* test c2r case

* New C2R Python layer normal and exception use cases

* Documentation of the common interfaces of c2r and c2c

* Unittest of op with FFT C2C, C2R and r2c added

Co-authored-by: lijiaqi <lijiaqi0612@163.com>

* add fft related options to CMakeLists.txt

* fix typos and clean code (#39)

* fix invisible character in mkl branch and fix error in error message

* clean code: remove docstring from unittest for signal.py.

* always convert numpy array to paddle.Tensor to avoid comparing numpy dtype with paddle dtype. (#40)

* always convert numpy array to paddle.Tensor to avoid comparing numpy dtype with paddle dtype.

* fix CI Errors: numpy dtype comparison, thrust when cuda is not available (#41)

1. always convert numpy array to paddle.Tensor to avoid comparing numpy dtype with paddle dtype.
2. promote floating point tensor to complex tensor ior fft_c2c and fft_c2r;
3. fix unittest to catch UnImplementedError and RuntimeError;
4. fix compile error by avoid using thrust when cuda is not available.
5.  fix sample code, use paddle.fft instead of paddle.tensor.fft

* remove inclusion of thrust, add __all__ list for fft (#42)

* Add api doc and update unittest. (#43)

* Add doc strings.
* Update overlap_add op unittest

* fix MKL-based FFT implementation (#44)

* fix MKL-based FFT implementation, MKL CDFT's FORWARD DOMAIN is always REAL for R2C and C2R

* remove code for debug (#45)

* use dynload for cufft (#46)

* use std::ptrdiff_t as datatype of stride (instead of int64_t) to avoid argument mismatch on some platforms.

* add complex support for fill_zeros_like

* use dynload for cufft

* Update doc and unittest. (#47)

* Add doc of frame op and overlap_add op.

* Update unittest.

* use dynload for cufft (#48)

1. use dynload for cufft
2. fix unittest;
3. temporarily disable Rocm.

* fix conflicts and merge upstream (#49)

fix conflicts and merge upstream

* fix compile error: only link dyload_cuda when cuda is available (PaddlePaddle#50)

* fix compile error: only link dyload_cuda when cuda is available

* fix dynload for cufft on windows (PaddlePaddle#51)

1. fix dynload for cufft on windows;
2. fix unittests.

* add NOMINMAX to compile on windows (PaddlePaddle#52)

 add NOMINMAX to compile on windows

* explicitly specify capture mode for lambdas (PaddlePaddle#55)

 explicitly specify capture mode for lambdas

* fix fft sample (PaddlePaddle#53)

* fix fft sample

* update scipy and numpy version for unittests of fft (PaddlePaddle#56)

update scipy and numpy version for unittests of fft

* Add static graph unittests of frame and overlap_add api. (PaddlePaddle#57)

* Remove cache of cuFFT & Disable ONEMKL (PaddlePaddle#59)

1. replace numpy.fft with scipy.fft as numpy<1.20 not support ortho norm
2. remove cache of cufft plans;
3. enhance error checking.
4. default WITH_ONEMKL to OFF

Co-authored-by: jeff41404 <jeff41404@gmail.com>
Co-authored-by: root <root@bjyz-sys-gpu-kongming9.bjyz.baidu.com>
Co-authored-by: KP <109694228@qq.com>
Co-authored-by: lijiaqi <lijiaqi0612@163.com>
Co-authored-by: Xiaoxu Chen <chenxx_id@163.com>
Co-authored-by: lijiaqi0612 <33169170+lijiaqi0612@users.noreply.github.com>
@thisjiang thisjiang deleted the add_piano_graph_executor branch December 10, 2021 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants