oneAPI Deep Neural Network Library (oneDNN)
Performance library for Deep Learning
2.1.3
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
59dnnl::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.
74struct 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
81inline 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.
85inline 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).
118inline 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).
126inline 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
133inline 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
155inline 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
162inline 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
168inline 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 {
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
232inline 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 {
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
#define DNNL_RUNTIME_SYCL
SYCL runtime.
Definition: dnnl_types.h:2627
cl_command_queue get_command_queue(const stream &astream)
Returns OpenCL queue object associated with the execution stream.
Definition: dnnl_ocl.hpp:108
cl_mem get_mem_object(const memory &amemory)
Returns the OpenCL memory object associated with the memory object.
Definition: dnnl_ocl.hpp:120
cl::sycl::queue get_queue(const stream &astream)
Returns the SYCL queue associated with an execution stream.
Definition: dnnl_sycl.hpp:132
memory_kind get_memory_kind(const memory &amemory)
Returns the memory allocation kind associated with a memory object.
Definition: dnnl_sycl.hpp:204
@ buffer
Buffer memory allocation kind.
@ usm
USM (device, shared, host, or unknown) memory allocation kind.
An execution engine.
Definition: dnnl.hpp:869
kind
Kinds of engines.
Definition: dnnl.hpp:874
@ gpu
GPU engine.
@ cpu
CPU engine.
static size_t get_count(kind akind)
Returns the number of engines of a certain kind.
Definition: dnnl.hpp:893
oneDNN exception class.
Definition: dnnl.hpp:84
const char * what() const noexcept override
Returns the explanatory string.
Definition: dnnl.hpp:96
size_t get_size() const
Returns size of the memory descriptor in bytes.
Definition: dnnl.hpp:2200
Memory object.
Definition: dnnl.hpp:1108
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
desc get_desc() const
Returns the associated memory descriptor.
Definition: dnnl.hpp:2270
void * get_data_handle() const
Returns the underlying memory buffer.
Definition: dnnl.hpp:2289
std::vector< dim > dims
Vector of dimensions.
Definition: dnnl.hpp:1115
An execution stream.
Definition: dnnl.hpp:985