oneAPI Deep Neural Network Library (oneDNN)
Performance library for Deep Learning
2.1.0
example_utils.hpp
1 /*******************************************************************************
2 * Copyright 2019-2021 Intel Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16 
17 #ifndef EXAMPLE_UTILS_HPP
18 #define EXAMPLE_UTILS_HPP
19 
20 #include <algorithm>
21 #include <cassert>
22 #include <functional>
23 #include <iostream>
24 #include <numeric>
25 #include <stdexcept>
26 #include <stdlib.h>
27 #include <string>
28 #include <initializer_list>
29 
30 #include "dnnl.hpp"
31 #include "dnnl_debug.h"
32 
33 #if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL
34 #include "dnnl_ocl.hpp"
35 #elif DNNL_GPU_RUNTIME == DNNL_RUNTIME_SYCL
36 #include "dnnl_sycl.hpp"
37 #endif
38 
39 #if DNNL_CPU_THREADING_RUNTIME == DNNL_RUNTIME_OMP
40 
41 #ifdef _MSC_VER
42 #define PRAGMA_MACRo(x) __pragma(x)
43 #define PRAGMA_MACRO(x) PRAGMA_MACRo(x)
44 #else
45 #define PRAGMA_MACRo(x) _Pragma(#x)
46 #define PRAGMA_MACRO(x) PRAGMA_MACRo(x)
47 #endif
48 
49 // MSVC doesn't support collapse clause in omp parallel
50 #if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER)
51 #define collapse(x)
52 #endif
53 
54 #define PRAGMA_OMP_PARALLEL_FOR_COLLAPSE(n) PRAGMA_MACRO(omp parallel for collapse(n))
55 #else // DNNL_CPU_THREADING_RUNTIME == DNNL_RUNTIME_OMP
56 #define PRAGMA_OMP_PARALLEL_FOR_COLLAPSE(n)
57 #endif
58 
59 dnnl::engine::kind validate_engine_kind(dnnl::engine::kind akind) {
60  // Checking if a GPU exists on the machine
61  if (akind == dnnl::engine::kind::gpu) {
63  std::cout << "Application couldn't find GPU, please run with CPU "
64  "instead.\n";
65  exit(0);
66  }
67  }
68  return akind;
69 }
70 
71 // Exception class to indicate that the example uses a feature that is not
72 // available on the current systems. It is not treated as an error then, but
73 // just notifies a user.
74 struct example_allows_unimplemented : public std::exception {
75  example_allows_unimplemented(const char *message) noexcept
76  : message(message) {}
77  const char *what() const noexcept override { return message; }
78  const char *message;
79 };
80 
81 inline const char *engine_kind2str_upper(dnnl::engine::kind kind);
82 
83 // Runs example function with signature void() and catches errors.
84 // Returns `0` on success, `1` or oneDNN error, and `2` on example error.
85 inline int handle_example_errors(
86  std::initializer_list<dnnl::engine::kind> engine_kinds,
87  std::function<void()> example) {
88  int exit_code = 0;
89 
90  try {
91  example();
92  } catch (example_allows_unimplemented &e) {
93  std::cout << e.message << std::endl;
94  exit_code = 0;
95  } catch (dnnl::error &e) {
96  std::cout << "oneDNN error caught: " << std::endl
97  << "\tStatus: " << dnnl_status2str(e.status) << std::endl
98  << "\tMessage: " << e.what() << std::endl;
99  exit_code = 1;
100  } catch (std::exception &e) {
101  std::cout << "Error in the example: " << e.what() << "." << std::endl;
102  exit_code = 2;
103  }
104 
105  std::string engine_kind_str;
106  for (auto it = engine_kinds.begin(); it != engine_kinds.end(); ++it) {
107  if (it != engine_kinds.begin()) engine_kind_str += "/";
108  engine_kind_str += engine_kind2str_upper(*it);
109  }
110 
111  std::cout << "Example " << (exit_code ? "failed" : "passed") << " on "
112  << engine_kind_str << "." << std::endl;
113  return exit_code;
114 }
115 
116 // Same as above, but for functions with signature
117 // void(dnnl::engine::kind engine_kind, int argc, char **argv).
118 inline int handle_example_errors(
119  std::function<void(dnnl::engine::kind, int, char **)> example,
120  dnnl::engine::kind engine_kind, int argc, char **argv) {
121  return handle_example_errors(
122  {engine_kind}, [&]() { example(engine_kind, argc, argv); });
123 }
124 
125 // Same as above, but for functions with signature void(dnnl::engine::kind).
126 inline int handle_example_errors(
127  std::function<void(dnnl::engine::kind)> example,
128  dnnl::engine::kind engine_kind) {
129  return handle_example_errors(
130  {engine_kind}, [&]() { example(engine_kind); });
131 }
132 
133 inline dnnl::engine::kind parse_engine_kind(
134  int argc, char **argv, int extra_args = 0) {
135  // Returns default engine kind, i.e. CPU, if none given
136  if (argc == 1) {
137  return validate_engine_kind(dnnl::engine::kind::cpu);
138  } else if (argc <= extra_args + 2) {
139  std::string engine_kind_str = argv[1];
140  // Checking the engine type, i.e. CPU or GPU
141  if (engine_kind_str == "cpu") {
142  return validate_engine_kind(dnnl::engine::kind::cpu);
143  } else if (engine_kind_str == "gpu") {
144  return validate_engine_kind(dnnl::engine::kind::gpu);
145  }
146  }
147 
148  // If all above fails, the example should be ran properly
149  std::cout << "Inappropriate engine kind." << std::endl
150  << "Please run the example like this: " << argv[0] << " [cpu|gpu]"
151  << (extra_args ? " [extra arguments]" : "") << "." << std::endl;
152  exit(1);
153 }
154 
155 inline const char *engine_kind2str_upper(dnnl::engine::kind kind) {
156  if (kind == dnnl::engine::kind::cpu) return "CPU";
157  if (kind == dnnl::engine::kind::gpu) return "GPU";
158  assert(!"not expected");
159  return "<Unknown engine>";
160 }
161 
162 inline dnnl::memory::dim product(const dnnl::memory::dims &dims) {
163  return std::accumulate(dims.begin(), dims.end(), (dnnl::memory::dim)1,
164  std::multiplies<dnnl::memory::dim>());
165 }
166 
167 // Read from memory, write to handle
168 inline void read_from_dnnl_memory(void *handle, dnnl::memory &mem) {
169  dnnl::engine eng = mem.get_engine();
170  size_t size = mem.get_desc().get_size();
171 
172  if (!handle) throw std::runtime_error("handle is nullptr.");
173 
174 #ifdef DNNL_WITH_SYCL
175  bool is_cpu_sycl = (DNNL_CPU_RUNTIME == DNNL_RUNTIME_SYCL
176  && eng.get_kind() == dnnl::engine::kind::cpu);
177  bool is_gpu_sycl = (DNNL_GPU_RUNTIME == DNNL_RUNTIME_SYCL
178  && eng.get_kind() == dnnl::engine::kind::gpu);
179  if (is_cpu_sycl || is_gpu_sycl) {
180  auto mkind = dnnl::sycl_interop::get_memory_kind(mem);
182  auto buffer = dnnl::sycl_interop::get_buffer<uint8_t>(mem);
183  auto src = buffer.get_access<cl::sycl::access::mode::read>();
184  uint8_t *src_ptr = src.get_pointer();
185  if (!src_ptr)
186  throw std::runtime_error("get_pointer returned nullptr.");
187  for (size_t i = 0; i < size; ++i)
188  ((uint8_t *)handle)[i] = src_ptr[i];
189  } else {
190  assert(mkind == dnnl::sycl_interop::memory_kind::usm);
191  uint8_t *src_ptr = (uint8_t *)mem.get_data_handle();
192  if (!src_ptr)
193  throw std::runtime_error("get_data_handle returned nullptr.");
194  if (is_cpu_sycl) {
195  for (size_t i = 0; i < size; ++i)
196  ((uint8_t *)handle)[i] = src_ptr[i];
197  } else {
198  auto sycl_queue
200  sycl_queue.memcpy(src_ptr, handle, size).wait();
201  }
202  }
203  return;
204  }
205 #endif
206 #if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL
207  if (eng.get_kind() == dnnl::engine::kind::gpu) {
208  dnnl::stream s(eng);
209  cl_command_queue q = dnnl::ocl_interop::get_command_queue(s);
210  cl_mem m = dnnl::ocl_interop::get_mem_object(mem);
211 
212  cl_int ret = clEnqueueReadBuffer(
213  q, m, CL_TRUE, 0, size, handle, 0, NULL, NULL);
214  if (ret != CL_SUCCESS)
215  throw std::runtime_error("clEnqueueReadBuffer failed.");
216  return;
217  }
218 #endif
219 
220  if (eng.get_kind() == dnnl::engine::kind::cpu) {
221  uint8_t *src = static_cast<uint8_t *>(mem.get_data_handle());
222  if (!src) throw std::runtime_error("get_data_handle returned nullptr.");
223  for (size_t i = 0; i < size; ++i)
224  ((uint8_t *)handle)[i] = src[i];
225  return;
226  }
227 
228  assert(!"not expected");
229 }
230 
231 // Read from handle, write to memory
232 inline void write_to_dnnl_memory(void *handle, dnnl::memory &mem) {
233  dnnl::engine eng = mem.get_engine();
234  size_t size = mem.get_desc().get_size();
235 
236  if (!handle) throw std::runtime_error("handle is nullptr.");
237 
238 #ifdef DNNL_WITH_SYCL
239  bool is_cpu_sycl = (DNNL_CPU_RUNTIME == DNNL_RUNTIME_SYCL
240  && eng.get_kind() == dnnl::engine::kind::cpu);
241  bool is_gpu_sycl = (DNNL_GPU_RUNTIME == DNNL_RUNTIME_SYCL
242  && eng.get_kind() == dnnl::engine::kind::gpu);
243  if (is_cpu_sycl || is_gpu_sycl) {
244  auto mkind = dnnl::sycl_interop::get_memory_kind(mem);
246  auto buffer = dnnl::sycl_interop::get_buffer<uint8_t>(mem);
247  auto dst = buffer.get_access<cl::sycl::access::mode::write>();
248  uint8_t *dst_ptr = dst.get_pointer();
249  if (!dst_ptr)
250  throw std::runtime_error("get_pointer returned nullptr.");
251  for (size_t i = 0; i < size; ++i)
252  dst_ptr[i] = ((uint8_t *)handle)[i];
253  } else {
254  assert(mkind == dnnl::sycl_interop::memory_kind::usm);
255  uint8_t *dst_ptr = (uint8_t *)mem.get_data_handle();
256  if (!dst_ptr)
257  throw std::runtime_error("get_data_handle returned nullptr.");
258  if (is_cpu_sycl) {
259  for (size_t i = 0; i < size; ++i)
260  dst_ptr[i] = ((uint8_t *)handle)[i];
261  } else {
262  auto sycl_queue
264  sycl_queue.memcpy(dst_ptr, handle, size).wait();
265  }
266  }
267  return;
268  }
269 #endif
270 #if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL
271  if (eng.get_kind() == dnnl::engine::kind::gpu) {
272  dnnl::stream s(eng);
273  cl_command_queue q = dnnl::ocl_interop::get_command_queue(s);
274  cl_mem m = dnnl::ocl_interop::get_mem_object(mem);
275 
276  cl_int ret = clEnqueueWriteBuffer(
277  q, m, CL_TRUE, 0, size, handle, 0, NULL, NULL);
278  if (ret != CL_SUCCESS)
279  throw std::runtime_error("clEnqueueWriteBuffer failed.");
280  return;
281  }
282 #endif
283 
284  if (eng.get_kind() == dnnl::engine::kind::cpu) {
285  uint8_t *dst = static_cast<uint8_t *>(mem.get_data_handle());
286  if (!dst) throw std::runtime_error("get_data_handle returned nullptr.");
287  for (size_t i = 0; i < size; ++i)
288  dst[i] = ((uint8_t *)handle)[i];
289  return;
290  }
291 
292  assert(!"not expected");
293 }
294 
295 #endif
void * get_data_handle() const
Returns the underlying memory buffer.
Definition: dnnl.hpp:2289
cl_mem get_mem_object(const memory &amemory)
Returns the OpenCL memory object associated with the memory object.
Definition: dnnl_ocl.hpp:120
static size_t get_count(kind akind)
Returns the number of engines of a certain kind.
Definition: dnnl.hpp:893
#define DNNL_RUNTIME_SYCL
SYCL runtime.
Definition: dnnl_types.h:2627
Buffer memory allocation kind.
An execution engine.
Definition: dnnl.hpp:869
oneDNN exception class.
Definition: dnnl.hpp:84
const char * what() const noexcept override
Returns the explanatory string.
Definition: dnnl.hpp:96
kind
Kinds of engines.
Definition: dnnl.hpp:874
dnnl_dim_t dim
Integer type for representing dimension sizes and indices.
Definition: dnnl.hpp:1112
engine get_engine() const
Returns the associated engine.
Definition: dnnl.hpp:2278
cl::sycl::queue get_queue(const stream &astream)
Returns the SYCL queue associated with an execution stream.
Definition: dnnl_sycl.hpp:132
Memory object.
Definition: dnnl.hpp:1108
memory_kind get_memory_kind(const memory &amemory)
Returns the memory allocation kind associated with a memory object.
Definition: dnnl_sycl.hpp:204
desc get_desc() const
Returns the associated memory descriptor.
Definition: dnnl.hpp:2270
cl_command_queue get_command_queue(const stream &astream)
Returns OpenCL queue object associated with the execution stream.
Definition: dnnl_ocl.hpp:108
USM (device, shared, host, or unknown) memory allocation kind.
std::vector< dim > dims
Vector of dimensions.
Definition: dnnl.hpp:1115
size_t get_size() const
Returns size of the memory descriptor in bytes.
Definition: dnnl.hpp:2200
An execution stream.
Definition: dnnl.hpp:985