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