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

ENH: unify PADDLE_ENFORCE for cublas, cudnn, curand #2883

Merged
merged 12 commits into from
Jul 19, 2017

Conversation

gangliao
Copy link
Contributor

No description provided.

@@ -74,8 +75,7 @@ class CUDADeviceContext : public DeviceContext {
public:
explicit CUDADeviceContext(const GPUPlace gpu_place) : gpu_place_(gpu_place) {
GPUPlaceGuard guard(gpu_place_);
paddle::platform::throw_on_error(cudaStreamCreate(&stream_),
"cudaStreamCreate failed");
PADDLE_ENFORCE(cudaStreamCreate(&stream_), "cudaStreamCreate failed");
Copy link
Collaborator

Choose a reason for hiding this comment

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

PADDLE_ENFORCE means the condition must be true. But CUXXXX_STATUS_SUCCESS is zero, and false. So it seems that these lines should be

PADDLE_ENFORCE(cudaStreamCreate(&stream_) == CUDA_SUCCESS);

Copy link
Contributor Author

@gangliao gangliao Jul 15, 2017

Choose a reason for hiding this comment

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

In my opinion, PADDLE_ENFORCE(condition, ...) means condition must execute correctly. No matter the return of condition is 0 or 1, the internal of PADDLE_ENFORCE can figure out how to deal with it.

Copy link
Member

@QiJune QiJune Jul 17, 2017

Choose a reason for hiding this comment

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

Agree with @reyoung. I check on how glog does. What CHECK does is to check the condition is true.
And we can implement CUDNN_ENFORCE/CUBLAS_ENFORCE based on PADDLE_ENFORCE for writing related codes more conveniently

Copy link
Collaborator

@wangkuiyi wangkuiyi Jul 17, 2017

Choose a reason for hiding this comment

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

Given that

PADDLE_ENFORCE(cudaStreamCreate(&stream_))

is shorter than

PADDLE_ENFORCE(cudaStreamCreate(&stream_) == CUDA_SUCCESS);

and it is compatible with the use of the word "enforce" in English, I support that we adopt @gangliao 's proposal -- to make PADDLE_ENFORCE handle the condition.

I agree the English word CHECK should be followed by a boolean value and expects to be true. My point is that ENFORCE should be followed by an action, which is hopefully successful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, @wangkuiyi

PADDLE_ENFORCE(cudaStreamCreate(&stream_) == CUDA_SUCCESS);

will also impede us to print apposite error info for cuda, cudnn, cublas, curand.


template <typename... Args>
inline void throw_on_error(cudaError_t e, const Args&... args) {
if (e) {
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 this snippet

  if (e) {
    std::stringstream ss;
    ss << ::paddle::string::Sprintf(args...);
    ss << ::paddle::string::Sprintf(" at [%s:%s];", __FILE__, __LINE__);
    throw thrust::system_error(e, thrust::cuda_category(), ss.str());
  }

can be rewritten as

  if (e) {
    throw thrust::system_error(e, thrust::cuda_category(), 
                               string::Sprintf(args...) + string::Sprintf(" at "...));
  }

I assume that we can write string::Sprintf other than ::paddle::string::Sprintf.

std::string all_msg_;
};

// From https://stackoverflow.com/questions/30130930/
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 rephrase this comment:

// Because most enforce conditions would evaluate to true, we can use 
// __builtin_expect to instruct the C++ compiler to generate code that always forces 
// branch prediction of true. This generates faster binary code. For more details, 
// please check https://stackoverflow.com/a/43870188/724872. __builtin_expect
// is since C++11.

template <typename... Args>
inline void throw_on_error(int stat, const Args&... args) {
if (UNLIKELY(!(stat))) {
PADDLE_THROW(args...);
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 we don't really need PADDLE_THROW or EnforceNotMet; instead, we can call

throw std::runtime_error(...);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I totally agree with you, I will remove EnforceNotMet and PADDLE_THROW

#define PADDLE_THROW(...) \
do { \
throw ::paddle::platform::EnforceNotMet( \
::paddle::string::Sprintf(__VA_ARGS__), __FILE__, __LINE__); \
Copy link
Collaborator

Choose a reason for hiding this comment

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

I noticed that paddle::string doesn't call paddle::platform now, so it is safe to make paddle::platform call paddle::string. I think this is the right calling direction. Thanks!

#ifndef PADDLE_ONLY_CPU

template <typename... Args>
inline void throw_on_error(cudaError_t e, const Args&... args) {
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 we don't make these throw_on_error variants inline functions, but just normal functions, so we could move their implementations into a .cc file and make this header files concise and clean? It seems that this wouldn't degrade the performance of PADDLE_ENFORECE.

It looks to me that it's reasonable to keep this header file short; currently, the #ifndef PADDLE_ONLY_CPU check spans over too many lines.

Copy link
Contributor Author

@gangliao gangliao Jul 19, 2017

Choose a reason for hiding this comment

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

That's a good idea, but enforce.h is always frequently used in almost all source files.
If we separate it to *.h,*.cc, then almost all files have to compile with libenforce.a as follows:

cc_library(each_file SRCS each_file.cc DEPS enforce)

That's quite annoying.

#endif // PADDLE_ONLY_CPU

template <typename... Args>
inline void throw_on_error(int stat, const Args&... args) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

In addition to a int stat variant, should we also have a bool variant? I noticed applications like

PADDLE_ENFORCE(paddle::platform::is_cpu_place(self.place()),
                        "Only CPU tensor can cast to numpy array");

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will double check this, because we already use bool condition in our tensor implementation, but it didn't throw a error.

@QiJune
Copy link
Member

QiJune commented Jul 19, 2017

The pr modifies many files, does it modify automatically or by hand?

@gangliao
Copy link
Contributor Author

@QiJune Using IDE or Modern editor like VS CODE can easily achieve this.

@gangliao gangliao merged commit 635ac11 into PaddlePaddle:develop Jul 19, 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.

4 participants