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 operator base #2725

Merged
merged 34 commits into from
Jul 11, 2017
Merged

add operator base #2725

merged 34 commits into from
Jul 11, 2017

Conversation

jacquesqiao
Copy link
Member

No description provided.

explicit Operator(const OpDesc& desc);
virtual ~Operator() {}

void InitializeAttrs();
Copy link
Member Author

Choose a reason for hiding this comment

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

maybe we can remove this interface and do initialize in the ctor

namespace paddle {
namespace framework {

OperatorBase::OperatorBase(const OpDesc &desc): desc_(desc) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems that OpBase doesn't take whole OpDesc as the arguments, but only inputs and outputs.

So maybe std::vector<string> input and std::vector<string> output is enough.

Copy link
Member Author

Choose a reason for hiding this comment

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

done, remove construct function

}

std::string OperatorBase::DebugString() {
return desc_.DebugString();
Copy link
Collaborator

Choose a reason for hiding this comment

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

DebugString() is not in OpDesc.

It should be generated in run time.

Like Operator Cosine, from (var_input1, var_input2) to (var_output)

}
}

std::string OperatorBase::DebugString() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, const is need.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

return desc_.DebugString();
}

Variable* OperatorBase::input(Scope *scope, int index) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It should not be a part of the interface.

Because the invoker of Operator is not need input method.

Copy link
Member Author

Choose a reason for hiding this comment

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

done, will put input() into OpRunContext


/// initialize Attributes of this OP from proto message desc.attrs()
/// you should derive this function to init the attr you need in OP.
virtual void InitializeAttributes() = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

That interface is not needed, due to @Canpio 's design.

Copy link
Member Author

Choose a reason for hiding this comment

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

done, move to CreateOperator

Variable* output(Scope* scope, int index);

protected:
const OpDesc desc_;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Op do not store OpDesc

Copy link
Member Author

Choose a reason for hiding this comment

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

why not store OpDesc, it will not change and can use directly when to serialize.

Copy link
Member Author

Choose a reason for hiding this comment

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

done.... though i still want to keep it

Variable* input(Scope* scope, int index);
Variable* output(Scope* scope, int index);

protected:
Copy link
Collaborator

Choose a reason for hiding this comment

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

inputs_ and outputs_ are all public.

@@ -12,3 +12,4 @@ cc_test(op_proto_test SRCS op_proto_test.cc DEPS op_proto protobuf)

proto_library(op_desc SRCS op_desc.proto DEPS attr_type)
cc_test(op_desc_test SRCS op_desc_test.cc DEPS op_desc protobuf)
cc_library(operator SRCS operator.cc)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need unittest.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

* device resource such as CUDA stream, cublas handle, etc. from
* OpRunContext. User should construct it before run the Operator.
*/
class OpRunContext {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Given that Caffe2 doesn't have OpRunContext, it seems that we are doing over-engineering here. How about put Scope* scope in the base class Context, which has sub-classes CPUContext and GPUContext?

namespace framework {

class DeviceContext {};
class CpuContext : public DeviceContext {};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Cpu => CPU. It is an acronym.


class DeviceContext {};
class CpuContext : public DeviceContext {};
class GpuContext : public DeviceContext {};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Gpu => GPU

DeviceContext* device_context;
};

using Attribute =
Copy link
Collaborator

@wangkuiyi wangkuiyi Jul 7, 2017

Choose a reason for hiding this comment

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

I can see three ways of implementing Operator attributes:

  1. std::map<string, boost:variant<...> >
  2. std::map<string, shared_ptr<Variable> >
  3. OperatorDesc, given that OperatorBase::OperatorBase takes a OperatorDesc and saves it in desc_.

Which is the simplest one?

It looks to me that the last one is the simplest because the former two introduce a new form, std::map, of OperatorDesc::attrs_.

/// InferShape infer the size of Variables used by this Operator with
/// information
/// inside scope
virtual void InferShape(const Scope* scope) const = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

InferShape should have default implementation?

virtual void InferShape(const Scope* scope) {}

Also, it might change properties of inputs and outputs, so shouldn't be suffixed with const?


std::string DebugString() const;

public:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why should we expose all these fields? I don't seem at any chance the outside world need to access them.

Indeed, even from inside an Operator class, we don't need to access them. Instead, the following methods should use them:

template <typename T> const T& GetAttr(std::string name);
Variable* Input(std::string name) const;
Variable* output(std::string name) const;

Copy link
Member Author

@jacquesqiao jacquesqiao Jul 7, 2017

Choose a reason for hiding this comment

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

caffe里面是把这几个函数放在了OpBase中,因为caffe的op在new的时候把workspace和context都创建好放在了op里边,所以caffe op的run不需要加什么参数。

我们目前scope和devicecontext都是运行时才能确定,通过组装成一个 OpRunContext 给 Run(OpRunContext* context) ,所以目前的想法是这几个函数都有OpRunContext提供:

这样的话,很自然的,我们就会有OpKernel这个概念了,Operator存储一些非运行时的配置,运行之前组装OpRunContext传给OpKernel的run。

class OpKernel {
  void Run(OpRunContext* context) {
    auto input0 =  context.Input(0);
    auto cuda_stream = contex.cuda_stream();
  }
}

* device resource such as CUDA stream, cublas handle, etc. from
* OpRunContext. User should construct it before run the Operator.
*/
class OpRunContext {
Copy link
Contributor

@Superjomn Superjomn Jul 7, 2017

Choose a reason for hiding this comment

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

OpContext in mxnet, why not call it OpContext given that their functions are the same? OpRunContext too long and sounds strange.

Copy link
Member Author

Choose a reason for hiding this comment

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

done


std::string DebugString() const;

public:
Copy link
Contributor

Choose a reason for hiding this comment

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

protected if they need to be accessed easily by subclasses better ?

Copy link
Member Author

Choose a reason for hiding this comment

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

add getter function and set all this to private

* device resource such as CUDA stream, cublas handle, etc. from
* OpRunContext. User should construct it before run the Operator.
*/
class OpRunContext {
Copy link
Contributor

Choose a reason for hiding this comment

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

class OpRunContext -> struct better ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe OpContext will be more complex later, and will add more interface.

}
for(auto it = attrs.begin(); it != attrs.end(); ++it) {
attrs_[it->first] = it->second;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The declaration of attrs_:

AttributeMap attrs_;

The type is same with attrs, so why not use attrs_ = attrs?


inline const AttributeMap attrs() const {
return attrs_;
}
Copy link
Contributor

@qingqing01 qingqing01 Jul 7, 2017

Choose a reason for hiding this comment

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

template const T& GetAttr(std::string name);

如wangyi老师所说这样觉得更好些,返回Attribute、AttributeMap对于写Op的人来说也还需要查怎么获取值~

Copy link
Member Author

Choose a reason for hiding this comment

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

done

OpDesc desc_;
std::vector<std::string> inputs_;
std::vector<std::string> outputs_;
AttributeMap attrs_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Each operator should have a unique name. Is the name saved in attrs_ or desc_?

Copy link
Member Author

Choose a reason for hiding this comment

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

there is no uniq name now, maybe we can add one

#include <boost/variant.hpp>
#include <string>
#include <unordered_map>
#include <vector>
Copy link
Contributor

Choose a reason for hiding this comment

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

The above four lines can be removed. They have been included in paddle/framework/attr_checker.h.

virtual void Run(OpContext* context) const = 0;

private:
OpDesc desc_;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is OpDesc used for some functions?

Copy link
Member Author

@jacquesqiao jacquesqiao Jul 10, 2017

Choose a reason for hiding this comment

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

to get type of op

inline const OpDesc desc() const { return desc_; }

return boost::get<T>(attrs_.at(name));
}

inline const std::vector<std::string> inputs() const { return inputs_; }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Reference.

const std::vector<std::string>&.

Also, I suggest all private data member of OpBase should be public. Not to add noising Getter

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the inputs_ are owned by an operator itself, better be a protected if subclasses want to edit, but with a const getter by visitors that just want to read, such as a Net?


void OperatorBase::InferShape(Scope* scope) const {}

const std::string OperatorBase::DebugString() const {
Copy link
Contributor

@Superjomn Superjomn Jul 10, 2017

Choose a reason for hiding this comment

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

why should this be a const?

the returned string is copied, and free to mutate?

Copy link
Member Author

Choose a reason for hiding this comment

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

const removed


inline const std::vector<std::string> inputs() const { return inputs_; }

inline const std::vector<std::string> outputs() const { return outputs_; }
Copy link
Contributor

Choose a reason for hiding this comment

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

do not need an inline here? a function defined within a class should be set inline by the compiler automatically.


inline const std::vector<std::string> outputs() const { return outputs_; }

const std::string DebugString() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

const is useless here.

Copy link
Member Author

Choose a reason for hiding this comment

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

removed

void InferShape(Scope* scope) const;

void Run(std::shared_ptr<Scope> scope, DeviceContext* dev_ctx) {
OpContext* op_ctx = new OpContext(this, scope, dev_ctx);
Copy link
Contributor

Choose a reason for hiding this comment

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

new here, no need to manage the memory?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

namespace paddle {
namespace framework {

void OperatorBase::InferShape(std::shared_ptr<Scope> scope) const {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

const T&

Variable* Output(int index) const;

public:
const OperatorBase* op;
Copy link
Collaborator

Choose a reason for hiding this comment

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

op -> op_;


public:
const OperatorBase* op;
std::shared_ptr<Scope> scope;
Copy link
Collaborator

Choose a reason for hiding this comment

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

scope -> scope_

public:
const OperatorBase* op;
std::shared_ptr<Scope> scope;
DeviceContext* device_context;
Copy link
Collaborator

Choose a reason for hiding this comment

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

device_context -> device_context_;

virtual ~OperatorBase() {}

template <typename T>
inline const T GetAttr(const std::string& name) const {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Useless function. boost::get<T>(attrs_.at(name)) is enough. Even this function is needed, it should belong to AttributeMap not OperatorBase

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, this function should return const T& if needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I plan to store attrs as variable later, this interface is used for now

virtual void InferShape(std::shared_ptr<Scope> scope) const;

/// Net will call this function to Run an op.
void Run(std::shared_ptr<Scope> scope, DeviceContext* dev_ctx) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

const std::shared_ptr&

Copy link
Member Author

Choose a reason for hiding this comment

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

done


/// when implement an Op, your should implement this function.
/// this function should be moved to OpKernel later
virtual void Run(OpContext* context) const = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

OpContext --> OpRunContext

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, this function is not in OperatorBase interface. The OperatorBase interface should only contains

virtual void Run(const std::shared_ptr<Scope>& scope, DeviceContext* dev_ctx) = 0;

Copy link
Member Author

@jacquesqiao jacquesqiao Jul 11, 2017

Choose a reason for hiding this comment

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

yes, but now we have no kernel OP, and this interface is used by test now, so I keep it and will move it to op with kernel in the next PR

virtual void Run(OpContext* context) const = 0;

public:
OpDesc desc_;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this field needed now?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, I think we need this field.

Copy link
Collaborator

@reyoung reyoung left a comment

Choose a reason for hiding this comment

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

LGTM

@jacquesqiao jacquesqiao merged commit a2e5f65 into PaddlePaddle:develop Jul 11, 2017
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.

7 participants