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 <cstring> |
23 | #include <functional> |
24 | #include <iostream> |
25 | #include <numeric> |
26 | #include <stdexcept> |
27 | #include <stdlib.h> |
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) { |
62 | if (dnnl::engine::get_count(dnnl::engine::kind::gpu) == 0) { |
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); |
181 | if (mkind == dnnl::sycl_interop::memory_kind::buffer) { |
182 | auto buffer = dnnl::sycl_interop::get_buffer<uint8_t>(mem); |
183 | auto src = buffer.get_access<::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 |
199 | = dnnl::sycl_interop::get_queue(dnnl::stream(eng)); |
200 | sycl_queue.memcpy(handle, src_ptr, 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 | void *mapped_ptr = mem.map_data(); |
209 | if (mapped_ptr) std::memcpy(handle, mapped_ptr, size); |
210 | mem.unmap_data(mapped_ptr); |
211 | return; |
212 | } |
213 | #endif |
214 | |
215 | if (eng.get_kind() == dnnl::engine::kind::cpu) { |
216 | uint8_t *src = static_cast<uint8_t *>(mem.get_data_handle()); |
217 | if (!src) throw std::runtime_error("get_data_handle returned nullptr." ); |
218 | for (size_t i = 0; i < size; ++i) |
219 | ((uint8_t *)handle)[i] = src[i]; |
220 | return; |
221 | } |
222 | |
223 | assert(!"not expected" ); |
224 | } |
225 | |
226 | // Read from handle, write to memory |
227 | inline void write_to_dnnl_memory(void *handle, dnnl::memory &mem) { |
228 | dnnl::engine eng = mem.get_engine(); |
229 | size_t size = mem.get_desc().get_size(); |
230 | |
231 | if (!handle) throw std::runtime_error("handle is nullptr." ); |
232 | |
233 | #ifdef DNNL_WITH_SYCL |
234 | bool is_cpu_sycl = (DNNL_CPU_RUNTIME == DNNL_RUNTIME_SYCL |
235 | && eng.get_kind() == dnnl::engine::kind::cpu); |
236 | bool is_gpu_sycl = (DNNL_GPU_RUNTIME == DNNL_RUNTIME_SYCL |
237 | && eng.get_kind() == dnnl::engine::kind::gpu); |
238 | if (is_cpu_sycl || is_gpu_sycl) { |
239 | auto mkind = dnnl::sycl_interop::get_memory_kind(mem); |
240 | if (mkind == dnnl::sycl_interop::memory_kind::buffer) { |
241 | auto buffer = dnnl::sycl_interop::get_buffer<uint8_t>(mem); |
242 | auto dst = buffer.get_access<::sycl::access::mode::write>(); |
243 | uint8_t *dst_ptr = dst.get_pointer(); |
244 | if (!dst_ptr) |
245 | throw std::runtime_error("get_pointer returned nullptr." ); |
246 | for (size_t i = 0; i < size; ++i) |
247 | dst_ptr[i] = ((uint8_t *)handle)[i]; |
248 | } else { |
249 | assert(mkind == dnnl::sycl_interop::memory_kind::usm); |
250 | uint8_t *dst_ptr = (uint8_t *)mem.get_data_handle(); |
251 | if (!dst_ptr) |
252 | throw std::runtime_error("get_data_handle returned nullptr." ); |
253 | if (is_cpu_sycl) { |
254 | for (size_t i = 0; i < size; ++i) |
255 | dst_ptr[i] = ((uint8_t *)handle)[i]; |
256 | } else { |
257 | auto sycl_queue |
258 | = dnnl::sycl_interop::get_queue(dnnl::stream(eng)); |
259 | sycl_queue.memcpy(dst_ptr, handle, size).wait(); |
260 | } |
261 | } |
262 | return; |
263 | } |
264 | #endif |
265 | #if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL |
266 | if (eng.get_kind() == dnnl::engine::kind::gpu) { |
267 | void *mapped_ptr = mem.map_data(); |
268 | if (mapped_ptr) std::memcpy(mapped_ptr, handle, size); |
269 | mem.unmap_data(mapped_ptr); |
270 | return; |
271 | } |
272 | #endif |
273 | |
274 | if (eng.get_kind() == dnnl::engine::kind::cpu) { |
275 | uint8_t *dst = static_cast<uint8_t *>(mem.get_data_handle()); |
276 | if (!dst) throw std::runtime_error("get_data_handle returned nullptr." ); |
277 | for (size_t i = 0; i < size; ++i) |
278 | dst[i] = ((uint8_t *)handle)[i]; |
279 | return; |
280 | } |
281 | |
282 | assert(!"not expected" ); |
283 | } |
284 | |
285 | #endif |
286 | |