-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
Feature/error handling in paddle #1149
Feature/error handling in paddle #1149
Conversation
@wangkuiyi @hedaoyuan 我们用这个Status对象做Paddle的错误值合适么? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
另外,写个ISSUE描述一下这个工作吧。最主要的是,要让别人明白Status怎么用;以及要让别人知道,后续写哪些东西的时候要用Status作为返回值。
* @brief isOK | ||
* @return true if OK. | ||
*/ | ||
inline bool isOK() const noexcept { return errMsg_ == nullptr; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(!errMsg_), compares shared_ptr with a null pointer
http://en.cppreference.com/w/cpp/memory/shared_ptr/operator_cmp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
其实,感觉maybe这两种写法在release模式下没有啥性能区别。。。我回头测试一下。。
XXX==nullptr感觉可读性反而好一点。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
测试了一下,判断shared_ptr是否为空。使用隐式类型转换成bool型,或者直接和nullptr比较,在clang 8里,-O3下,二者生成的汇编完全一致。没有差别。
感觉还是用 xxx == nullptr 或者 xxx != nullptr 可读性好一点。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
使用隐式类型转换成bool型
!errMsg_ 并不是隐式类型转换,是调用了operator bool();errMsg_ == nullptr;
也是调用了!errMsg_。
7) !lhs
8) !rhs
9) (bool)lhs
10) (bool)rhs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
恩,都是inline的调用啦,-O3都会没有的 :-)
|
||
#include <gtest/gtest.h> | ||
|
||
TEST(Status, testAll) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TEST 除了check Status本身以外,还可以去构造一些实际用途的Case。
比如,构造一些函数调用,错误后返回Status这样的例子,也能比较好的让别人知道Status的用法。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
写到这个类的注释里面了。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
写到类的注释里面了。
*/ | ||
template <typename... ARGS> | ||
inline void setByPrintf(const char* fmt, ARGS... args) noexcept { | ||
constexpr size_t kBufferSize = 4096; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4096,需要这么大。。。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to 1024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
提了一些建议。我担心这个工作有点儿over engineering了。用了很多C++的路子。但是貌似都是C就能做的。
* Although Status inherits the std::exception, but do not throw it except you | ||
* know what you are doing. | ||
*/ | ||
class Status final : public std::exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为什么要从 std::exception
派生呢?定义成 更简单的形式,比如
class Error {
private:
const char *msg_;
}
不行吗?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
派生了也没啥坏处。。std::exception算是C++的通常惯例。。
const char* msg_;和这个事情是两件事,如果用const char* msg_; 那我们还得手写一个复制构造函数,而且Error的传递从之前的引用传递变成了值传递。
将msg_定义成shared_ptr,那么普通的赋值Error对象,就是引用传递了。这玩意其实是和golang里面的那个error语意一致的写法。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我了解需要 std::shared_ptrstd::string 的原因了。
回到派生——我建议从最简化的原则出发“如果没有特别的好处,我们就不要引入这个派生了“。
namespace paddle { | ||
|
||
/** | ||
* Status is Paddle error code. It only contain a std::string as error message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
如果只包含一个error message,那么叫Error就好了。比如Go的error type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
有道理。
* @brief set a error message for status. | ||
* @param msg | ||
*/ | ||
inline void set(const std::string& msg) noexcept { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
建议用一个全局friend函数(比如叫做Errorf),而不是用Error的method来创建内容。因为一个error的语法应该是“一旦创建不能被修改”,所以method,尤其是名为set的method不能贴切地表现这个syntax。
Error Network.allocateParameters() {
...
if (...) {
return Errorf("Cannot allocate memory block of size %d", kBufferSize);
}
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
* create a error status by C style printf. | ||
*/ | ||
template <typename... ARGS> | ||
inline static Status printf(const char* fmt, ARGS... args) noexcept { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不需要很多风格的error creation吧。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
/** | ||
* @brief what will return the error message. If status is OK, return nullptr. | ||
*/ | ||
const char* what() const noexcept override { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为什么需要 override 和 noexcept 关键词呢?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
因为这玩意其实还是继承了std::exception,符合标准的C++ exception的定义。
虽然我们不用Exception,但是借用这个接口定义我们的error应该是可以的。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
因为没有特别的好处,所以别派生了,读者也就不需要了解override和noexcept这两个关键字了。毕竟C++里的大部分发明都是利弊兼备的,读者也被code style帮助着尽量少了解这些利弊之争。
/** | ||
* @brief what will return the error message. If status is OK, return nullptr. | ||
*/ | ||
const char* what() const noexcept override { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what ==> msg?
这样用的时候就是 error.msg()
:
auto err = Errorf("...);
LOG(ERROR) << err.msg();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what函数名是C++对于exception的普遍约定。虽然Paddle不鼓励使用exception,但是按照C++的普遍约定命名也比较有道理。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
同上,不用exception,也就不用what了。
inline bool isOK() const noexcept { return errMsg_ == nullptr; } | ||
|
||
private: | ||
std::shared_ptr<std::string> errMsg_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里就是一个 const char * msg_
就好了吧。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在上面回复了一下。
* Also make ErrorF as a global method.
@wangkuiyi @hedaoyuan Partially follow comments. Done. |
贴一个我想到的简单实现:
|
02830cf
to
5a15c70
Compare
@wangkuiyi Follow comments, except use |
* use log(FATAL) or CHECK to make program exit before. When we clean all | ||
* log(FATAL) and CHECK in Paddle, 'check' method will be removed. | ||
*/ | ||
class Error final { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不需要final吧?有些C++程序员不知道final。而且这里也非必须final。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/** | ||
* Default Status. OK | ||
*/ | ||
inline Error() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里不需要inline。如果是为了优化编译性能,尽量让编译器去决定性能吧。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/** | ||
* @brief Create an Error use printf syntax. | ||
*/ | ||
inline explicit Error(const char* fmt, ...) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里不需要inline。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
class Error final { | ||
public: | ||
/** | ||
* Default Status. OK |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个comment写得是不是太简洁了?下面这样?
@brief Construct an no-error value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
va_start(ap, fmt); | ||
constexpr size_t kBufferSize = 1024; | ||
this->errMsg_.reset(new std::string(kBufferSize, 0)); | ||
auto sz = vsnprintf(&(*errMsg_)[0], kBufferSize, fmt, ap); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这样写暴露了string的太多实现细节了——string里有一个连续的buffer。没有必要。我之前那样写更简短。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/** | ||
* @brief what will return the error message. If no error, return nullptr. | ||
*/ | ||
inline const char* msg() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不需要inline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
inline void check() const { CHECK(*this) << msg(); } | ||
|
||
private: | ||
std::shared_ptr<std::string> errMsg_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class已经叫error了,这里只需要叫msg_。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
* @note It is a temp method used during cleaning Paddle code. It will be | ||
* removed later. | ||
*/ | ||
inline void check() const { CHECK(*this) << msg(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error是新加入的代码,为什么要等着“remove later”?应该一开始就不要制造需要remove的问题吧?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
因为Paddle内部充斥着LOG(Fatal)和CHECK。我们不太可能在一个PR里面,把这些东西全部替换掉。。只能逐层的一步一步来,先替换掉内层的CHECK和LOG(FATAL),返回给外层。
但是,这样一步一步来的时候,在外层处理这个Error就需要还调用CHECK。这里这个函数就是方便外层去检查这个Error的。
最终,我们的Paddle会将这个Error,全部返回给最上层的调用(例如Main函数或者Trainer中),这样我们就可以把这个check去掉,然后在一个地方写这个CHECK了。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
理解了。好的。
*/ | ||
inline const char* msg() const { | ||
if (errMsg_) { | ||
return errMsg_->data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里应该用c_str,不可以用 data: http://stackoverflow.com/questions/194634/string-c-str-vs-data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/** | ||
* @brief operator bool, return True if there is no error. | ||
*/ | ||
inline operator bool() const { return !errMsg_; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
因为msg调用的c_str可能运行代价比较高(如果string内部使用的不是连续buffer),所以这里应该延承 error::error() 的 syntax,用 msg_.get() != NULL 作为判断标准。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里实际上调用的是shared_ptr是不是空指针。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我明白了。
不过这是在我知道C++里可以重载operator (),并且去 文档 里查阅,确定了shared_ptr重载了operator () 的情况下,才能理解这个用法。如果我不查阅文档,我甚至不确定 shared_ptr 重载的 operator () 是返回的 bool 类型还是 pointer 类型。
所以我建议这里还是用朴素的写法:msg_.get() != NULL
。
我们的原则是希望源码的读者(维护着)具备最少的知识,就能理解我们的代码。
我之前贴的那个例子,没有办法构造“no error”。受到 @reyoung 的修改的启发,更新一下我的那个例子:
以下代码适合改成unit test,放在
|
4bb9eb7
to
f3739dc
Compare
f3739dc
to
c88dec2
Compare
* update install doc for release 1.5.2, test=develop * update Tables.md for 1.5.2, test=develop * update Tables.md to 1.5.1 for windows, test=develop
错误处理应该是Paddle API比较重要的一方面,因为把Paddle作为一个库引入的话,调几个函数就log(FATAL)了肯定不合适。
这里的Status是不是可以暂定为Paddle的错误类型?Status基本上是一个std::string的指针。如果为空,那么便没有error,否则error的详细信息是这个字符串的内容。
这种实现下,如果没有错误的话,返回这个错误码是基本没有性能开销的(和返回一个整数开销应该一样)。如果返回错误的话,开销在指针引用上(主体可能是多线程的shared_ptr锁),不会复制字符串很多次。