Caffe2 - C++ API
A deep learning, cross platform ML framework
common_cudnn.h
1 
17 #ifndef CAFFE2_CORE_COMMON_CUDNN_H_
18 #define CAFFE2_CORE_COMMON_CUDNN_H_
19 
20 #include <array>
21 #include <mutex>
22 
23 #include <cudnn.h>
24 
25 #include "caffe2/core/common.h"
26 #include "caffe2/core/context.h"
27 #include "caffe2/core/logging.h"
28 #include "caffe2/core/types.h"
29 #include "caffe2/proto/caffe2.pb.h"
30 
31 static_assert(
32  CUDNN_VERSION >= 5000,
33  "Caffe2 requires cudnn version 5.0 or above.");
34 
35 #if CUDNN_VERSION < 6000
36 #pragma message "CUDNN version under 6.0 is supported at best effort."
37 #pragma message "We strongly encourage you to move to 6.0 and above."
38 #pragma message "This message is intended to annoy you enough to update."
39 #endif // CUDNN_VERSION < 6000
40 
41 #define CUDNN_VERSION_MIN(major, minor, patch) \
42  (CUDNN_VERSION >= ((major) * 1000 + (minor) * 100 + (patch)))
43 
44 namespace caffe2 {
45 
46 namespace internal {
50 inline const char* cudnnGetErrorString(cudnnStatus_t status) {
51  switch (status) {
52  case CUDNN_STATUS_SUCCESS:
53  return "CUDNN_STATUS_SUCCESS";
54  case CUDNN_STATUS_NOT_INITIALIZED:
55  return "CUDNN_STATUS_NOT_INITIALIZED";
56  case CUDNN_STATUS_ALLOC_FAILED:
57  return "CUDNN_STATUS_ALLOC_FAILED";
58  case CUDNN_STATUS_BAD_PARAM:
59  return "CUDNN_STATUS_BAD_PARAM";
60  case CUDNN_STATUS_INTERNAL_ERROR:
61  return "CUDNN_STATUS_INTERNAL_ERROR";
62  case CUDNN_STATUS_INVALID_VALUE:
63  return "CUDNN_STATUS_INVALID_VALUE";
64  case CUDNN_STATUS_ARCH_MISMATCH:
65  return "CUDNN_STATUS_ARCH_MISMATCH";
66  case CUDNN_STATUS_MAPPING_ERROR:
67  return "CUDNN_STATUS_MAPPING_ERROR";
68  case CUDNN_STATUS_EXECUTION_FAILED:
69  return "CUDNN_STATUS_EXECUTION_FAILED";
70  case CUDNN_STATUS_NOT_SUPPORTED:
71  return "CUDNN_STATUS_NOT_SUPPORTED";
72  case CUDNN_STATUS_LICENSE_ERROR:
73  return "CUDNN_STATUS_LICENSE_ERROR";
74  default:
75  return "Unknown cudnn error number";
76  }
77 }
78 } // namespace internal
79 
80 // A macro that wraps around a cudnn statement so we can check if the cudnn
81 // execution finishes or not.
82 #define CUDNN_ENFORCE(condition) \
83  do { \
84  cudnnStatus_t status = condition; \
85  CAFFE_ENFORCE_EQ( \
86  status, \
87  CUDNN_STATUS_SUCCESS, \
88  ", Error at: ", \
89  __FILE__, \
90  ":", \
91  __LINE__, \
92  ": ", \
93  ::caffe2::internal::cudnnGetErrorString(status)); \
94  } while (0)
95 #define CUDNN_CHECK(condition) \
96  do { \
97  cudnnStatus_t status = condition; \
98  CHECK(status == CUDNN_STATUS_SUCCESS) \
99  << ::caffe2::internal::cudnnGetErrorString(status); \
100  } while (0)
101 
102 // report the version of cuDNN Caffe2 was compiled with
103 inline size_t cudnnCompiledVersion() {
104  return CUDNN_VERSION;
105 }
106 // report the runtime version of cuDNN
107 inline size_t cudnnRuntimeVersion() {
108  return cudnnGetVersion();
109 }
110 
111 // Check compatibility of compiled and runtime cuDNN versions
112 inline void CheckCuDNNVersions() {
113  // Version format is major*1000 + minor*100 + patch
114  // Major, minor and patch versions must all match
115  bool version_match = cudnnCompiledVersion() == cudnnRuntimeVersion();
116  CAFFE_ENFORCE(version_match,
117  "cuDNN compiled (", cudnnCompiledVersion(), ") and "
118  "runtime (", cudnnRuntimeVersion(), ") versions mismatch");
119 }
120 
126 template <typename T>
128 
129 template <>
130 class cudnnTypeWrapper<float> {
131  public:
132  static const cudnnDataType_t type = CUDNN_DATA_FLOAT;
133  typedef const float ScalingParamType;
134  typedef float BNParamType;
135  static ScalingParamType* kOne() {
136  static ScalingParamType v = 1.0;
137  return &v;
138  }
139  static const ScalingParamType* kZero() {
140  static ScalingParamType v = 0.0;
141  return &v;
142  }
143 };
144 
145 #if CUDNN_VERSION_MIN(6, 0, 0)
146 template <>
147 class cudnnTypeWrapper<int> {
148  public:
149  static const cudnnDataType_t type = CUDNN_DATA_INT32;
150  typedef const int ScalingParamType;
151  typedef int BNParamType;
152  static ScalingParamType* kOne() {
153  static ScalingParamType v = 1;
154  return &v;
155  }
156  static const ScalingParamType* kZero() {
157  static ScalingParamType v = 0;
158  return &v;
159  }
160 };
161 #endif // CUDNN_VERSION_MIN(6, 0, 0)
162 
163 template <>
164 class cudnnTypeWrapper<double> {
165  public:
166  static const cudnnDataType_t type = CUDNN_DATA_DOUBLE;
167  typedef const double ScalingParamType;
168  typedef double BNParamType;
169  static ScalingParamType* kOne() {
170  static ScalingParamType v = 1.0;
171  return &v;
172  }
173  static ScalingParamType* kZero() {
174  static ScalingParamType v = 0.0;
175  return &v;
176  }
177 };
178 
179 template <>
180 class cudnnTypeWrapper<float16> {
181  public:
182  static const cudnnDataType_t type = CUDNN_DATA_HALF;
183  typedef const float ScalingParamType;
184  typedef float BNParamType;
185  static ScalingParamType* kOne() {
186  static ScalingParamType v = 1.0;
187  return &v;
188  }
189  static ScalingParamType* kZero() {
190  static ScalingParamType v = 0.0;
191  return &v;
192  }
193 };
194 
199 inline cudnnTensorFormat_t GetCudnnTensorFormat(const StorageOrder& order) {
200  switch (order) {
201  case StorageOrder::NHWC:
202  return CUDNN_TENSOR_NHWC;
203  case StorageOrder::NCHW:
204  return CUDNN_TENSOR_NCHW;
205  default:
206  LOG(FATAL) << "Unknown cudnn equivalent for order: " << order;
207  }
208  // Just to suppress compiler warnings
209  return CUDNN_TENSOR_NCHW;
210 }
211 
218  public:
220  CUDNN_ENFORCE(cudnnCreateTensorDescriptor(&desc_));
221  }
222  ~cudnnTensorDescWrapper() noexcept {
223  CUDNN_CHECK(cudnnDestroyTensorDescriptor(desc_));
224  }
225 
226  inline cudnnTensorDescriptor_t Descriptor(
227  const cudnnTensorFormat_t format,
228  const cudnnDataType_t type,
229  const vector<int>& dims,
230  bool* changed) {
231  if (type_ == type && format_ == format && dims_ == dims) {
232  // if not changed, simply return the current descriptor.
233  if (changed)
234  *changed = false;
235  return desc_;
236  }
237  CAFFE_ENFORCE_EQ(
238  dims.size(), 4, "Currently only 4-dimensional descriptor supported.");
239  format_ = format;
240  type_ = type;
241  dims_ = dims;
242  CUDNN_ENFORCE(cudnnSetTensor4dDescriptor(
243  desc_,
244  format,
245  type,
246  dims_[0],
247  (format == CUDNN_TENSOR_NCHW ? dims_[1] : dims_[3]),
248  (format == CUDNN_TENSOR_NCHW ? dims_[2] : dims_[1]),
249  (format == CUDNN_TENSOR_NCHW ? dims_[3] : dims_[2])));
250  if (changed)
251  *changed = true;
252  return desc_;
253  }
254 
255  template <typename T>
256  inline cudnnTensorDescriptor_t Descriptor(
257  const StorageOrder& order,
258  const vector<int>& dims) {
259  return Descriptor(
260  GetCudnnTensorFormat(order), cudnnTypeWrapper<T>::type, dims, nullptr);
261  }
262 
263  private:
264  cudnnTensorDescriptor_t desc_;
265  cudnnTensorFormat_t format_;
266  cudnnDataType_t type_;
267  vector<int> dims_;
268  DISABLE_COPY_AND_ASSIGN(cudnnTensorDescWrapper);
269 };
270 
272  public:
274  CUDNN_ENFORCE(cudnnCreateFilterDescriptor(&desc_));
275  }
276  ~cudnnFilterDescWrapper() noexcept {
277  CUDNN_CHECK(cudnnDestroyFilterDescriptor(desc_));
278  }
279 
280  inline cudnnFilterDescriptor_t Descriptor(
281  const StorageOrder& order,
282  const cudnnDataType_t type,
283  const vector<int>& dims,
284  bool* changed) {
285  if (type_ == type && order_ == order && dims_ == dims) {
286  // if not changed, simply return the current descriptor.
287  if (changed)
288  *changed = false;
289  return desc_;
290  }
291  CAFFE_ENFORCE_EQ(
292  dims.size(), 4, "Currently only 4-dimensional descriptor supported.");
293  order_ = order;
294  type_ = type;
295  dims_ = dims;
296  CUDNN_ENFORCE(cudnnSetFilter4dDescriptor(
297  desc_,
298  type,
299  GetCudnnTensorFormat(order),
300  dims_[0],
301  // TODO - confirm that this is correct for NHWC
302  (order == StorageOrder::NCHW ? dims_[1] : dims_[3]),
303  (order == StorageOrder::NCHW ? dims_[2] : dims_[1]),
304  (order == StorageOrder::NCHW ? dims_[3] : dims_[2])));
305  if (changed)
306  *changed = true;
307  return desc_;
308  }
309 
310  template <typename T>
311  inline cudnnFilterDescriptor_t Descriptor(
312  const StorageOrder& order,
313  const vector<int>& dims) {
314  return Descriptor(order, cudnnTypeWrapper<T>::type, dims, nullptr);
315  }
316 
317  private:
318  cudnnFilterDescriptor_t desc_;
319  StorageOrder order_;
320  cudnnDataType_t type_;
321  vector<int> dims_;
322  DISABLE_COPY_AND_ASSIGN(cudnnFilterDescWrapper);
323 };
324 
325 
326 } // namespace caffe2
327 
328 #endif // CAFFE2_CORE_COMMON_CUDNN_H_
cudnnTensorFormat_t GetCudnnTensorFormat(const StorageOrder &order)
A wrapper function to convert the Caffe storage order to cudnn storage order enum values...
Definition: common_cudnn.h:199
Copyright (c) 2016-present, Facebook, Inc.
cudnnTensorDescWrapper is the placeholder that wraps around a cudnnTensorDescriptor_t, allowing us to do descriptor change as-needed during runtime.
Definition: common_cudnn.h:217
cudnnTypeWrapper is a wrapper class that allows us to refer to the cudnn type in a template function...
Definition: common_cudnn.h:127