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

basic scope implementation #2629

Merged
merged 12 commits into from
Jun 29, 2017
Merged

Conversation

jacquesqiao
Copy link
Member

@jacquesqiao jacquesqiao commented Jun 27, 2017

Basic implementation of Scope.

@jacquesqiao jacquesqiao changed the title scope-impl basic scope implementation Jun 27, 2017
@@ -0,0 +1,50 @@
#pragma once
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need copyright information

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

@@ -0,0 +1,55 @@
#include "paddle/framework/scope.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need copyright information.

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


// Create Variable in this Scope. Return error if Variable already been
// created.
Error __must_check CreateVariable(const std::string& name);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I checked the definition of __must_check. It means "must use the returned value", and has nothing about "check".

Also, according to Google C++ style, a macro should be all captital, e.g., MUST_USED_RETVALUE. It breaks the style.

Should we remove paddle/platform/must_check.h?

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, I will remove __must_check and return Variable directly

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


// Create Variable in this Scope. Return error if Variable already been
// created.
Error __must_check CreateVariable(const std::string& name);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I checked Caffe2's source code of the similar functionality at here:

Blob* Workspace::CreateBlob(const string& name) {
  if (HasBlob(name)) {
    VLOG(1) << "Blob " << name << " already exists. Skipping.";
  } else {
    VLOG(1) << "Creating blob " << name;
    blob_map_[name] = unique_ptr<Blob>(new Blob());
  }
  return GetBlob(name);
}

I am impressed that Caffe2's authors tried to handle the error as much as they could inside the function definition. Whereas, we simply return an error and use __must_check to require users of our functions to handle the error. The best place to handle the error is at where it happens, where we have the most sufficient information to recover, to report, or to fail.

I'd suggest that we use:

Variable* CreateVar(const std::string& name);

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

Copy link
Member Author

Choose a reason for hiding this comment

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

To give an accurate semantics, I prefer to provide two interfaces:

  • CreateVariable will check and fail if there already have a variable with the same name in the scope.
  • GetOrCreateVariable is used to get a variable anyway.
    Currently, I think the user should know exactly what they want.

Copy link
Collaborator

@wangkuiyi wangkuiyi Jun 29, 2017

Choose a reason for hiding this comment

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

Good argue!

Let us think about this following the first principle.

What users need are

  1. Create
  2. Get

Create could err if there has been a variable with the same name there. We can do either of the following things with this error:

  1. we do nothing
  2. return the error

A basic design principle is that we handle the error as much as we could at where it happens, and try not to return to the users, because the users knows nothing about the detail and cannot really do much with this error. Also, if the variable is already there, users' intention (create the variable) is fulfilled, it doesn't worthy of returning an error. So I prefer Create takes no-op with the duplication error.

Similarly, Get could err if it cannot find the variable, and we can take the following possible actions:

  1. no-op
  2. return error
  3. create the variable

When the user Gets a variable, it assumes that the variable should have been there. If it has not, it implies that the creation process failed or we were creating a wrong network description. Anyway, there do have an error so we cannot take no-op. Similarly, if we create the variable, we hide the error; this is not right. So we must report error. Fortunately, reporting the error is simple -- just return nullptr would be enough.

Summarize above derivation, I think we should have

  1. CreateVar, which does nothing if there has been the variable, and
  2. Get, which returns nullptr if the variable isn't there.

Copy link
Member Author

Choose a reason for hiding this comment

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

if the variable is already there, users' intention (create the variable) is fulfilled, there is really nothing worth of returning.

This line convinced me, thanks! I will try to learn more about the first principle.

Variable* GetVariable(const std::string& name) const;

// find and return Variables in the scope it self.
Variable* GetVarLocally(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.

In what situation we need GetVarLocally instead of GetVaraible?

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


// Get a Variable from Scope, if the Variable is not exist then create it.
// User should call this function most of time.
Variable* GetOrCreateVariable(const std::string& name);
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about merge CreateVariable and GetOrCreateVariable into one -- CreateVar?

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

Copy link
Member Author

Choose a reason for hiding this comment

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

To give an accurate semantics, I prefer to provide two interfaces:

  • CreateVariable will check and fail if there already have a variable with the same name in the scope.
  • GetOrCreateVariable is used to get a variable anyway.
    Currently, I think the user should know exactly what they want.

// User should call this function most of time.
Variable* GetOrCreateVariable(const std::string& name);

bool HaveVariable(const std::string& name);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have => Has, because the implied subject is "a workspace", not "workspaces" or something like that.

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

#include <unordered_map>
#include <vector>
#include "paddle/framework/variable.h"
#include "paddle/utils/Error.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see that this header file needs a special Error type. I think if we follow below comments, we can remove this inclusion.

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

namespace framework {

Variable* Scope::CreateVariable(const std::string& name) {
if (!HasVariable(name)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If HasVariable, just ASSERT_FALSE is better.

Copy link
Collaborator

Choose a reason for hiding this comment

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

And local scope could create a variable that uses the same name in the parent. It means the local variable can hide parent variable.

void CreateVariable(const std::string& name) {
  auto it = vars_.find(name);
  ASSERT_EQ(it, vars_.end());
  vars_[name] = std::unique_ptr<Variable>(new Variable());
}

Copy link
Member Author

Choose a reason for hiding this comment

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

In our design, CreateVariable is equal to GetOrCreateVariable

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we should implement Get, Create by it means, and implement another method name GetOrCreateVariable().

Copy link
Collaborator

Choose a reason for hiding this comment

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

I prefer a concise interface. It takes the users only two lines to implement GetOrCreateVariable:

const Variable* v = ws.GetVar("something");
if (v == nullptr)
  ws.CreateVar("something");
// do something with v.

doesn't differ much from

const Variable* v = ws.GetOrCreateVar("something");
// do something with v.

}

Variable* Scope::GetVarLocally(const std::string& name) const {
if (vars_.count(name)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

vars_.count is not good.

auto it = vars_.find(name);
if (it != vars_.end()) {
  return it->second.get();
}
return nullptr;

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, It's better to use find than count. Thanks for reminding!

}
}

bool Scope::HasVariable(const std::string &name) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

HasVariable() const

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.

I think we could implement Scope in the header only. Not split them into cpp files.

Because function in the header could be inlined, but in cpp, the function cannot be inlined.

The scope function is simple and should be inlined.

*/
class Scope {
public:
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() = default;

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


explicit Scope(const std::shared_ptr<Scope>& scope) : parent_(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.

Destructor is not 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.

done

public:
Scope() {}

explicit Scope(const std::shared_ptr<Scope>& scope) : parent_(scope) {}
Copy link
Collaborator

@reyoung reyoung Jun 28, 2017

Choose a reason for hiding this comment

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

Maybe a single ctor is enough.

And ctor of Scope should be private. There should be another method create std::shared_ptr<Scope> like:

private:
  Scope(const std::shared_ptr<Scope>& parent): parent_(parent) {}
public:
  static std::shared_ptr<Scope> Create(const std::shared_ptr<Scope>& parent=nullptr) {
  return std::make_shared<Scope>(parent);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

not quite understand the reason to always create a shared_prt?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not the simplest way:

class Scope {
 public:
  Scope(Scope* parent) : parent_(parent) {}
 private:
  std::shared_ptr<Scope> parent_;
};

Copy link
Member Author

Choose a reason for hiding this comment

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

Because we want user to use Scope as shared_ptr(std::shared_ptr parent_), a create function will do this for user.

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 default argument


~Scope() {}

// Create Variable in this Scope. Return error if Variable already been
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use '///' or '/**', see doxygen style

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

public:
static std::shared_ptr<Scope> Create(
const std::shared_ptr<Scope>& parent = nullptr) {
return std::make_shared<Scope>(Scope(parent));
Copy link
Collaborator

Choose a reason for hiding this comment

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

std::make_shared<Scope>(parent); should enough.

Copy link
Collaborator

Choose a reason for hiding this comment

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

According to https://google.github.io/styleguide/cppguide.html#Default_Arguments, we shouldn't use default argument here.

My proposal in #2629 (comment) seems OK.

Copy link
Member Author

Choose a reason for hiding this comment

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

it will fail if the default consturctor is private:

In file included from /Users/qiaolongfei/project/paddle/paddle/framework/scope_test.cc:15:
In file included from /Users/qiaolongfei/project/paddle/paddle/framework/scope.h:17:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/string:439:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:628:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:2194:15: error: field of type 'paddle::framework::Scope' has private default constructor
              __second_(_VSTD::forward<_Args2>(_VSTD::get<_I2>(__second_args))...)
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:2457:15: note: in instantiation of function template specialization 'std::__1::__libcpp_compressed_pair_imp<std::__1::allocator<paddle::framework::Scope>, paddle::framework::Scope, 1>::__libcpp_compressed_pair_imp<std::__1::allocator<paddle::framework::Scope> &, const std::__1::shared_ptr<paddle::framework::Scope> &, 0, 0>' requested here
            : base(__pc, _VSTD::move(__first_args), _VSTD::move(__second_args),
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:3800:16: note: in instantiation of function template specialization 'std::__1::__compressed_pair<std::__1::allocator<paddle::framework::Scope>, paddle::framework::Scope>::__compressed_pair<std::__1::allocator<paddle::framework::Scope> &, const std::__1::shared_ptr<paddle::framework::Scope> &>' requested here
            :  __data_(piecewise_construct, _VSTD::forward_as_tuple(__a),
               ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:4411:26: note: in instantiation of function template specialization 'std::__1::__shared_ptr_emplace<paddle::framework::Scope, std::__1::allocator<paddle::framework::Scope> >::__shared_ptr_emplace<const std::__1::shared_ptr<paddle::framework::Scope> &>' requested here
    ::new(__hold2.get()) _CntrlBlk(__a2, _VSTD::forward<_Args>(__args)...);
                         ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:4775:29: note: in instantiation of function template specialization 'std::__1::shared_ptr<paddle::framework::Scope>::make_shared<const std::__1::shared_ptr<paddle::framework::Scope> &>' requested here
    return shared_ptr<_Tp>::make_shared(_VSTD::forward<_Args>(__args)...);
                            ^
/Users/qiaolongfei/project/paddle/paddle/framework/scope.h:41:17: note: in instantiation of function template specialization 'std::__1::make_shared<paddle::framework::Scope, const std::__1::shared_ptr<paddle::framework::Scope> &>' requested here
    return std::make_shared<Scope>(parent);
                ^
/Users/qiaolongfei/project/paddle/paddle/framework/scope.h:35:12: note: declared private here
  explicit Scope(const std::shared_ptr<Scope>& parent = nullptr)
           ^
1 error generated.
make[2]: *** [paddle/framework/CMakeFiles/scope_test.dir/scope_test.cc.o] Error 1
make[1]: *** [paddle/framework/CMakeFiles/scope_test.dir/all] Error 2

Copy link
Member Author

Choose a reason for hiding this comment

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

default argument removed

/// Create Variable in this Scope. Failed if Variable already been
/// created.
Variable* CreateVariable(const std::string& name) {
PADDLE_ASSERT(!HasVariable(name));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should local scope hide parent scope variable?

If so, then here should be

PADDLE_ASSERT(vars_.find(name) == vars_.end());

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 am not sure why we need to hide parent scope variable

@wangkuiyi
Copy link
Collaborator

I approved because my questions were addressed. Please feel free to continue the discussion.

@jacquesqiao jacquesqiao merged commit 590795d into PaddlePaddle:develop Jun 29, 2017
heavengate pushed a commit to heavengate/Paddle that referenced this pull request Aug 16, 2021
* update readme en, test=document_fix

* update doc
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