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

tensorrt convert init #10144

Merged
merged 8 commits into from
May 4, 2018
Merged

tensorrt convert init #10144

merged 8 commits into from
May 4, 2018

Conversation

luotao1
Copy link
Contributor

@luotao1 luotao1 commented Apr 23, 2018

A implement of #10028 (comment)

The result of make test ARGS='-R tensorrt_convert_test -V':

76: [==========] Running 1 test from 1 test case.
76: [----------] Global test environment set-up.
76: [----------] 1 test from tensorrt
76: [ RUN      ] tensorrt.ConvertBlock
76: I0423 13:14:39.315845 17146 convert.cc:42] convert a fluid mul op to tensorrt fc layer without bias
76: I0423 13:14:39.315907 17146 convert.cc:42] convert a fluid Conv2d op to tensorrt conv layer without bias
76: [       OK ] tensorrt.ConvertBlock (0 ms)
76: [----------] 1 test from tensorrt (0 ms total)
76: 
76: [----------] Global test environment tear-down
76: [==========] 1 test from 1 test case ran. (1 ms total)
76: [  PASSED  ] 1 test.
1/1 Test #76: tensorrt_convert_test ............   Passed    5.66 sec

Will add TensorRTEngine as a member variable of TensorRTConverter class after #10003.

@luotao1 luotao1 requested a review from Superjomn April 23, 2018 13:33
void RegisterOpConverters();

// convert a fluid Mul op to tensorrt fc layer without bias
static void ConvertMul(const framework::OpDesc& op);
Copy link
Contributor

@Superjomn Superjomn Apr 24, 2018

Choose a reason for hiding this comment

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

Each op a static member function? better to be a normal function, or this class will have O(N) static member functions, weried.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 使用注册机制。

@@ -0,0 +1,51 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Copy link
Contributor

@Superjomn Superjomn Apr 24, 2018

Choose a reason for hiding this comment

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

why is a convert subdirectory needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

因为convert目录下会有convert_mul, convert_conv2d等多个op文件,所以采用目录形式。

}

void TensorRTConverter::RegisterOpConverters() {
op_registry_["mul"] = ConvertMul;
Copy link
Contributor

Choose a reason for hiding this comment

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

use a normal functor, just like fluid::Operator:

struct MulOpConverter : public OpConverter {
  void operator()(const OpDesc&);
};

REGISTER_TRT_OP_CONVETER("mul", MulOpConveter);

this way, it seems more natural to add more converters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 采用注册机制。

GetOpConverter()[#op_type] = new convert_class; \
} \
}; \
convert_class##Register convert_class##reg;
Copy link
Contributor

@Superjomn Superjomn Apr 25, 2018

Choose a reason for hiding this comment

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

static convert_class##Register convert_class##reg;

namespace inference {
namespace tensorrt {

class ConverterBase {
Copy link
Contributor

Choose a reason for hiding this comment

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

ConvertBase just have data, no need to be a base class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

virtual void Convert(const framework::OpDesc& op) = 0;
};

static std::unordered_map<std::string, OpConverter*>& GetOpConverter() {
Copy link
Contributor

Choose a reason for hiding this comment

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

OpConverter can be a unique class without inherit ConverterBase,
and it has no relation with TensorRTConverter, so no need to inherit the same class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,36 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

better to have the same file name with the operators.

For example, for conv2d_op.h, there is a convert/conv2d_op.h, so that one can see the relationship between them by search files.

}; \
convert_class##Register convert_class##reg;

class TensorRTConverter : public ConverterBase {
Copy link
Contributor

Choose a reason for hiding this comment

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

TensorRTConveter can be a unique class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


void Convert(const framework::OpDesc& op) {
std::string type = op.Type();
OpConverter& op_converter = this->register_op_converter_[type];
Copy link
Contributor

@Superjomn Superjomn Apr 25, 2018

Choose a reason for hiding this comment

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

auto& OpConverter& op_converter = OpConveter::register_op_converter_[type];

maybe this works.
register_op_conveter_ is a static member of OpConverter

@@ -0,0 +1,2 @@
nv_library(tensorrt_convert SRCS convert.cc mul_op.cc conv2d_op.cc DEPS dynload_cuda)
nv_test(test_tensorrt_convert SRCS test_convert.cc DEPS tensorrt paddle_fluid)
Copy link
Contributor

@Superjomn Superjomn Apr 26, 2018

Choose a reason for hiding this comment

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

paddle_fluid is quite a large dependency, and slow for compiling this test.

Just a small issue.

Copy link
Contributor

@Superjomn Superjomn left a comment

Choose a reason for hiding this comment

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

I made some changes, and it seems that the static member should be defined in .cc file again, so it can link.

I also remove the template from the static method Register.

diff --git a/paddle/fluid/inference/tensorrt/convert/convert.cc b/paddle/fluid/inference/tensorrt/convert/convert.cc
index 78a72b1..7b6123a 100644
--- a/paddle/fluid/inference/tensorrt/convert/convert.cc
+++ b/paddle/fluid/inference/tensorrt/convert/convert.cc
@@ -26,6 +26,9 @@ void TensorRTConverter::ConvertBlock(const framework::BlockDesc& block) {
   }
 }

+
+std::unordered_map<std::string, OpConverter> OpConverter::register_op_converter_;
+
 }  // namespace tensorrt
 }  // namespace inference
 }  // namespace paddle
diff --git a/paddle/fluid/inference/tensorrt/convert/convert.h b/paddle/fluid/inference/tensorrt/convert/convert.h
index 953086a..b9a8a01 100644
--- a/paddle/fluid/inference/tensorrt/convert/convert.h
+++ b/paddle/fluid/inference/tensorrt/convert/convert.h
@@ -32,13 +32,12 @@ class OpConverter {

   void Convert(const framework::OpDesc& op) {
     std::string type = op.Type();
-    OpConverter& op_converter = this->register_op_converter_[type];
+    OpConverter& op_converter = OpConverter::register_op_converter_[type];
     op_converter.Convert(op);
   }

-  template <typename T>
-  static void Register(const std::string key) {
-    register_op_converter_[key] = T();
+  static void Register(const std::string key, const OpConverter& v) {
+    register_op_converter_[key] = v;
   }
   static std::unordered_map<std::string, OpConverter> register_op_converter_;

@@ -52,7 +51,7 @@ class OpConverter {
 #define REGISTER_TRT_OP_CONVETER(op_type, convert_class)                \
   class convert_class : public OpConverter {                            \
    public:                                                              \
-    convert_class() { OpConverter::Register<convert_class>(#op_type); } \
+    convert_class() { OpConverter::Register(#op_type, convert_class()); } \
     void Convert(const framework::OpDesc& op);                          \
   }


template <typename T>
static void Register(const std::string key) {
register_op_converter_[key] = T();
Copy link
Contributor

Choose a reason for hiding this comment

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

registered_op_converter seems better.

@luotao1 luotao1 added the 预测 原名Inference,包含Capi预测问题等 label Apr 27, 2018
std::unordered_map<std::string, OpConverter*> converters_;

// fluid inference scope
framework::Scope* scope_;
Copy link
Contributor

Choose a reason for hiding this comment

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

framework::Scope* scope_{nullptr};

/*
* Convert Op from Fluid to TensorRT Engine.
*/
class OpConverter {
Copy link
Contributor

Choose a reason for hiding this comment

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

可以参考我pr里的写法,把单例和接口拆开。 之前王叔说过这个三种设计模式混在一起。

Copy link
Contributor

Choose a reason for hiding this comment

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

can be refactored latter when utils/singleton.h in the convert_io PR merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

#include "paddle/fluid/platform/device_context.h"
#include "paddle/fluid/platform/place.h"

USE_OP(relu);
Copy link
Contributor

Choose a reason for hiding this comment

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

Move this to bottom of the file, for that reader needn't care this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we move it to the bottom of the file, it seems no effect, and unit-test is a failure.

namespace inference {
namespace tensorrt {

void compare(float input, float expect) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Compare

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

PADDLE_ENFORCE_EQ(0, buffer_sizes_.count(name), "duplicate output name %s",
name);

auto* output = TensorRTEngine::GetITensor(name);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do all the outputs of a TensorRT layer can be retrieved by the name? Is this interface necessary given that there is already a DeclareOutput?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do all the outputs of a TensorRT layer can be retrieved by the name?

Yes, all the TensorRT layer will put its output into itensor_map_. The reason is that other TensorRT layer can get its input from itensor_map_ directly.

For example:
https://github.com/luotao1/Paddle/blob/beb1245560b26fd198c3bdd7063334ad933f2d89/paddle/fluid/inference/tensorrt/convert/activation_op.cc#L32

 engine_->SetITensor(op.Output("Out")[0], layer->getOutput(0));

Is this interface necessary given that there is already a DeclareOutput?

Yes, we can easily use engine->DeclareOutput("Out") to DeclareOutput. Maybe this function can have another name.

Copy link
Contributor

Choose a reason for hiding this comment

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

get

// Fill an ITensor into map itensor_map_.
void SetITensor(const std::string& name, nvinfer1::ITensor* tensor);
// Get an ITensor called name.
nvinfer1::ITensor* GetITensor(const std::string& name);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this interface necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we don't have GetITensor interface, we should directly call auto tensor = itensor_map_[name] to get the itensor, which has two disadvantages:

  • Each time we should call PADDLE_ENFORCE(itensor_map_.count(name), "no itensor %s", name); before.
  • itensor_map_ should be public.

Copy link
Contributor

@Superjomn Superjomn left a comment

Choose a reason for hiding this comment

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

LGTM

@luotao1 luotao1 merged commit 4646c0f into PaddlePaddle:develop May 4, 2018
@luotao1 luotao1 deleted the tr_convert_init branch May 7, 2018 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
预测 原名Inference,包含Capi预测问题等
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants