From f4281ceee47fca4a1002efeea93f9ba39f99de76 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 3 Jul 2017 14:27:37 +0800 Subject: [PATCH] renew simple_op_design --- doc/design/simple_op_design.md | 110 ++++++--------------------------- 1 file changed, 19 insertions(+), 91 deletions(-) diff --git a/doc/design/simple_op_design.md b/doc/design/simple_op_design.md index 93c0f68ca..49ca5db5d 100644 --- a/doc/design/simple_op_design.md +++ b/doc/design/simple_op_design.md @@ -43,10 +43,7 @@ message AttrValue { message AttrProto { required string name = 1; required string comment = 2; - optional AttrValue default = 3; - optional AttrValue max = 4; - optional AttrValue min = 5; - required AttrType type = 6; + required AttrType type = 3; }; message VarProto { @@ -63,7 +60,24 @@ message OpProto { }; ``` -The default value and value range didn't appear in out previous design. By adding these two fields, we are able to check attribute validity in Python and find out possible error as soon as possible. What's more, by providing the message about default value and value range to Python docstring, it helps to automatically generate more comprehensive documents. +To generate Python code automatically: + +```python +def create_python_ops_creatation_functions(): + op_protos = paddle.framework.OpRegistry.get_all_op_proto() + for type_name in op_protos: + op_proto = op_protos[type_name] + def __impl__(**kwargs): # User must use key word args in Paddle API + inputs = [kwargs.get(ipt.name, "") for ipt in op_proto.inputs] + outputs = [kwargs.get(opt.name, "") for opt in op_proto.outputs] + attrs = [cast_to_op_attr(attr, kwargs.get(attr.name, None)) for attr in op_proto.attrs] + opdesc = (input, outputs, type_name, attrs) + return paddle.framework.OpRegistry.CreateOp(opdesc) + __impl__.__doc__ = create_doc_string(op_proto) + globals()[type_name] = __impl__ + +create_python_ops_creatation_functions() +``` ### Message from Python to C++ @@ -185,89 +199,3 @@ def fc_layer(input, size, with_bias, activation): In above sample, `fc_op` and `sigmod_op` are low-level API. They build `OpDesc` and invoke corresponding C++ code. *TODO* - -## Op and Kernal - -After completely defined, an Op will be run in a network. However, Op's computing method may differ on different devices. One solution is that write an `Op`'s member function `Op::run()`, which contains computing methods of all possible devices. That may be a bad idea because we have to change all `Op`'s code to add a new device. - -Another choice is adding a concept named `kernal`. A `Kernal` describes an op's computing process on a certain device. After stripping `Variable` and `kernal`, `Op` becomes a pure conceptual class, which holds neither data nor detailed computing process. - -```cpp -class KernalBase { -public: - virtual void RunOnDevice(std::vector input_vars, - std::vector input_vars, - const OpAttrs* attrs) = 0; -}; - -template -class CosineKernal : public KernalBase { -public: - virtual void RunOnDevice(std::vector input_vars, - std::vector input_vars, - const OpAttrs* attrs) { - // no implementation - } -}; - -template <> -class CosineKernal : public KernalBase { -public: - virtual void RunOnDevice(std::vector input_vars, - std::vector input_vars, - const OpAttrs* attrs) { - CosineOpAttrs* cosine_attrs = static_cast(attrs); - // computing code - // ... - } -}; - -struct OpAttrs {...}; - -class Op { - public: - std::string get_kernal_name() { - return kernel_name_; - } - const vector& get_input_names() { - return input_names_; - } - const vector& get_output_names() { - return output_names_; - } - // ... - private: - std::vector input_names_; - std::vector output_names_; - std::string kernal_name_; - -} - -struct CosineOpAttrs : public OpAttrs { - float scale_; -} - -class CosineOp : public Op { - public: - const CosineOpAtrrs* get_attrs() { - return &attrs; - } - - private: - CosineOpAttrs attrs; -} - -RunOp(const Op& op, Scope scope) { - Kernal* kernal = get_kernal(scope, op.get_kernal_name()); - std::vector input_vars = - get_variables(scope, op.get_input_name()); - std::vector output_vars = - get_variables(scope, op.get_output_name()); - - kernal->RunOnDevice(input_vars, output_vars, op.get_attrs()); -} -``` - -All `Kernal` need to be registered beforehand, just like `Op`. - -Now, `Op` is no longer has `Run()` function. It only contains names of variables and kernels. During network running, `RunOp()` is called to invoke `Op`'s corresponding `Kernal`. `get_kernal()` is supposed to return `kernal` for current device. -- GitLab