blob: a009894438cde7208c46214a5c637d2b0a8d36eb [file] [log] [blame]
Georgios Pinitas8a5146f2021-01-12 15:51:07 +00001/*
2 * Copyright (c) 2021 Arm Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#ifndef ARM_COMPUTE_ACL_HPP_
25#define ARM_COMPUTE_ACL_HPP_
26
27#include "arm_compute/Acl.h"
28
29#include <cstdlib>
30#include <memory>
31#include <string>
Georgios Pinitas3f26ef42021-02-23 10:01:33 +000032#include <vector>
Georgios Pinitas8a5146f2021-01-12 15:51:07 +000033
34#if defined(ARM_COMPUTE_EXCEPTIONS_ENABLED)
35#include <exception>
36#endif /* defined(ARM_COMPUTE_EXCEPTIONS_ENABLED) */
37
38// Helper Macros
39#define ARM_COMPUTE_IGNORE_UNUSED(x) (void)(x)
40
41namespace acl
42{
43// Forward declarations
44class Context;
Georgios Pinitas3f26ef42021-02-23 10:01:33 +000045class Tensor;
46class TensorPack;
Georgios Pinitas8a5146f2021-01-12 15:51:07 +000047
48/**< Status code enum */
49enum class StatusCode
50{
51 Success = AclSuccess,
52 RuntimeError = AclRuntimeError,
53 OutOfMemory = AclOutOfMemory,
54 Unimplemented = AclUnimplemented,
55 UnsupportedTarget = AclUnsupportedTarget,
56 InvalidArgument = AclInvalidArgument,
57 InvalidTarget = AclInvalidTarget,
58 UnsupportedConfig = AclUnsupportedConfig,
59 InvalidObjectState = AclInvalidObjectState,
60};
61
62/**< Utility namespace containing helpers functions */
63namespace detail
64{
65/** Construct to handle destruction of objects
66 *
67 * @tparam T Object base type
68 */
69template <typename T>
70struct ObjectDeleter
71{
72};
73
74#define OBJECT_DELETER(obj, func) \
75 template <> \
76 struct ObjectDeleter<obj> \
77 \
78 { \
79 static inline AclStatus Destroy(obj v) \
80 { \
81 return func(v); \
82 } \
83 };
84
85OBJECT_DELETER(AclContext, AclDestroyContext)
Georgios Pinitas3f26ef42021-02-23 10:01:33 +000086OBJECT_DELETER(AclTensor, AclDestroyTensor)
87OBJECT_DELETER(AclTensorPack, AclDestroyTensorPack)
Georgios Pinitas8a5146f2021-01-12 15:51:07 +000088
89#undef OBJECT_DELETER
90
91/** Convert a strongly typed enum to an old plain c enum
92 *
93 * @tparam E Plain old C enum
94 * @tparam SE Strongly typed resulting enum
95 *
96 * @param[in] v Value to convert
97 *
98 * @return A corresponding plain old C enumeration
99 */
100template <typename E, typename SE>
101constexpr E as_cenum(SE v) noexcept
102{
103 return static_cast<E>(static_cast<typename std::underlying_type<SE>::type>(v));
104}
105
106/** Convert plain old enumeration to a strongly typed enum
107 *
108 * @tparam SE Strongly typed resulting enum
109 * @tparam E Plain old C enum
110 *
111 * @param[in] val Value to convert
112 *
113 * @return A corresponding strongly typed enumeration
114 */
115template <typename SE, typename E>
116constexpr SE as_enum(E val) noexcept
117{
118 return static_cast<SE>(val);
119}
120
121/** Object base class for library objects
122 *
123 * Class is defining basic common interface for all the library objects
124 *
125 * @tparam T Object type to be templated on
126 */
127template <typename T>
128class ObjectBase
129{
130public:
131 /** Destructor */
132 ~ObjectBase() = default;
133 /** Copy constructor */
134 ObjectBase(const ObjectBase<T> &) = default;
135 /** Move Constructor */
136 ObjectBase(ObjectBase<T> &&) = default;
137 /** Copy assignment operator */
138 ObjectBase<T> &operator=(const ObjectBase<T> &) = default;
139 /** Move assignment operator */
140 ObjectBase<T> &operator=(ObjectBase<T> &&) = default;
141 /** Reset object value
142 *
143 * @param [in] val Value to set
144 */
145 void reset(T *val)
146 {
147 _object.reset(val, detail::ObjectDeleter<T *>::Destroy);
148 }
149 /** Access uderlying object
150 *
151 * @return Underlying object
152 */
153 const T *get() const
154 {
155 return _object.get();
156 }
157 /** Access uderlying object
158 *
159 * @return Underlying object
160 */
161 T *get()
162 {
163 return _object.get();
164 }
165
166protected:
167 /** Constructor */
168 ObjectBase() = default;
169
170protected:
171 std::shared_ptr<T> _object{ nullptr }; /**< Library object */
172};
173
174/** Equality operator for library object
175 *
176 * @tparam T Parameter to template on
177 *
178 * @param[in] lhs Left hand-side argument
179 * @param[in] rhs Right hand-side argument
180 *
181 * @return True if objects are equal, else false
182 */
183template <typename T>
184bool operator==(const ObjectBase<T> &lhs, const ObjectBase<T> &rhs)
185{
186 return lhs.get() == rhs.get();
187}
188
189/** Inequality operator for library object
190 *
191 * @tparam T Parameter to template on
192 *
193 * @param[in] lhs Left hand-side argument
194 * @param[in] rhs Right hand-side argument
195 *
196 * @return True if objects are equal, else false
197 */
198template <typename T>
199bool operator!=(const ObjectBase<T> &lhs, const ObjectBase<T> &rhs)
200{
201 return !(lhs == rhs);
202}
203} // namespace detail
204
205#if defined(ARM_COMPUTE_EXCEPTIONS_ENABLED)
206/** Status class
207 *
208 * Class is an extension of std::exception and contains the underlying
209 * status construct and an error explanatory message to be reported.
210 *
211 * @note Class is visible only when exceptions are enabled during compilation
212 */
213class Status : public std::exception
214{
215public:
216 /** Constructor
217 *
218 * @param[in] status Status returned
219 * @param[in] msg Error message to be bound with the exception
220 */
221 Status(StatusCode status, const std::string &msg)
222 : _status(status), _msg(msg)
223 {
224 }
225 /** Returns an explanatory exception message
226 *
227 * @return Status message
228 */
229 const char *what() const noexcept override
230 {
231 return _msg.c_str();
232 }
233 /** Underlying status accessor
234 *
235 * @return Status code
236 */
237 StatusCode status() const
238 {
239 return _status;
240 }
241 /** Explicit status converter
242 *
243 * @return Status code
244 */
245 explicit operator StatusCode() const
246 {
247 return _status;
248 }
249
250private:
251 StatusCode _status; /**< Status code */
252 std::string _msg; /**< Status message */
253};
254
255/** Reports an error status and throws an exception object in case of failure
256 *
257 * @note This implementation is used when exceptions are enabled during compilation
258 *
259 * @param[in] status Status to report
260 * @param[in] msg Explanatory error messaged
261 *
262 * @return Status code
263 */
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000264static inline void report_status(StatusCode status, const std::string &msg)
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000265{
266 if(status != StatusCode::Success)
267 {
268 throw Status(status, msg);
269 }
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000270}
271#else /* defined(ARM_COMPUTE_EXCEPTIONS_ENABLED) */
272/** Reports a status code
273 *
274 * @note This implementation is used when exceptions are disabled during compilation
275 * @note Message is surpressed and not reported in this case
276 *
277 * @param[in] status Status to report
278 * @param[in] msg Explanatory error messaged
279 *
280 * @return Status code
281 */
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000282static inline void report_status(StatusCode status, const std::string &msg)
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000283{
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000284 ARM_COMPUTE_IGNORE_UNUSED(status);
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000285 ARM_COMPUTE_IGNORE_UNUSED(msg);
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000286}
287#endif /* defined(ARM_COMPUTE_EXCEPTIONS_ENABLED) */
288
289/**< Target enum */
290enum class Target
291{
292 Cpu = AclCpu, /**< Cpu target that leverages SIMD */
293 GpuOcl = AclGpuOcl /**< Gpu target that leverages OpenCL */
294};
295
296/**< Available execution modes */
297enum class ExecutionMode
298{
299 FastRerun = AclPreferFastRerun, /**< Prefer minimum latency in consecutive runs, might introduce higher startup times */
300 FastStart = AclPreferFastStart, /**< Prefer minimizing startup time */
301};
302
303/** Context class
304 *
305 * Context acts as a central aggregate service for further objects created from it.
306 * It provides, internally, common facilities in order to avoid the use of global
307 * statically initialized objects that can lead to important side-effect under
308 * specific execution contexts.
309 *
310 * For example context contains allocators for object creation, for further backing memory allocation,
311 * any serialization interfaces and other modules that affect the construction of objects,
312 * like program caches for OpenCL.
313 */
314class Context : public detail::ObjectBase<AclContext_>
315{
316public:
317 /**< Context options */
318 struct Options
319 {
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000320 static constexpr int32_t num_threads_auto = -1; /**< Allow runtime to specify number of threads */
321
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000322 /** Default Constructor
323 *
324 * @note By default no precision loss is enabled for operators
325 * @note By default the preferred execution mode is to favor multiple consecutive reruns of an operator
326 */
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000327 Options()
328 : Options(ExecutionMode::FastRerun /* mode */,
329 AclCpuCapabilitiesAuto /* caps */,
330 false /* enable_fast_math */,
331 nullptr /* kernel_config */,
332 num_threads_auto /* max_compute_units */,
333 nullptr /* allocator */)
334 {
335 }
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000336 /** Constructor
337 *
338 * @param[in] mode Execution mode to be used
339 * @param[in] caps Capabilities to be used
340 * @param[in] enable_fast_math Allow precision loss in favor of performance
341 * @param[in] kernel_config Kernel configuration file containing construction tuning meta-data
342 * @param[in] max_compute_units Max compute units that are expected to used
343 * @param[in] allocator Allocator to be used for internal memory allocation
344 */
345 Options(ExecutionMode mode,
346 AclTargetCapabilities caps,
347 bool enable_fast_math,
348 const char *kernel_config,
349 int32_t max_compute_units,
350 AclAllocator *allocator)
351 {
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000352 copts.mode = detail::as_cenum<AclExecutionMode>(mode);
353 copts.capabilities = caps;
354 copts.enable_fast_math = enable_fast_math;
355 copts.kernel_config_file = kernel_config;
356 copts.max_compute_units = max_compute_units;
357 copts.allocator = allocator;
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000358 }
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000359
360 AclContextOptions copts{};
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000361 };
362
363public:
364 /** Constructor
365 *
366 * @note Serves as a simpler delegate constructor
367 * @note As context options, default conservative options will be used
368 *
369 * @param[in] target Target to create context for
370 * @param[out] status Status information if requested
371 */
372 explicit Context(Target target, StatusCode *status = nullptr)
373 : Context(target, Options(), status)
374 {
375 }
376 /** Constructor
377 *
378 * @param[in] target Target to create context for
379 * @param[in] options Context construction options
380 * @param[out] status Status information if requested
381 */
382 Context(Target target, const Options &options, StatusCode *status = nullptr)
383 {
384 AclContext ctx;
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000385 const auto st = detail::as_enum<StatusCode>(AclCreateContext(&ctx, detail::as_cenum<AclTarget>(target), &options.copts));
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000386 reset(ctx);
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000387 report_status(st, "[Arm Compute Library] Failed to create context");
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000388 if(status)
389 {
390 *status = st;
391 }
392 }
393};
Georgios Pinitas3f26ef42021-02-23 10:01:33 +0000394
395/**< Data type enumeration */
396enum class DataType
397{
398 Unknown = AclDataTypeUnknown,
399 UInt8 = AclUInt8,
400 Int8 = AclInt8,
401 UInt16 = AclUInt16,
402 Int16 = AclInt16,
403 UInt32 = AclUint32,
404 Int32 = AclInt32,
405 Float16 = AclFloat16,
406 BFloat16 = AclBFloat16,
407 Float32 = AclFloat32,
408};
409
410/** Tensor Descriptor class
411 *
412 * Structure that contains all the required meta-data to represent a tensor
413 */
414class TensorDescriptor
415{
416public:
417 /** Constructor
418 *
419 * @param[in] shape Shape of the tensor
420 * @param[in] data_type Data type of the tensor
421 */
422 TensorDescriptor(const std::vector<int32_t> &shape, DataType data_type)
423 : _shape(shape), _data_type(data_type)
424 {
425 _cdesc.ndims = _shape.size();
426 _cdesc.shape = _shape.data();
427 _cdesc.data_type = detail::as_cenum<AclDataType>(_data_type);
428 _cdesc.strides = nullptr;
429 _cdesc.boffset = 0;
430 }
431 /** Get underlying C tensor descriptor
432 *
433 * @return Underlying structure
434 */
435 const AclTensorDescriptor *get() const
436 {
437 return &_cdesc;
438 }
439
440private:
441 std::vector<int32_t> _shape{};
442 DataType _data_type{};
443 AclTensorDescriptor _cdesc{};
444};
445
446/** Import memory types */
447enum class ImportType
448{
449 Host = AclImportMemoryType::AclHostPtr
450};
451
452/** Tensor class
453 *
454 * Tensor is an mathematical construct that can represent an N-Dimensional space.
455 *
456 * @note Maximum dimensionality support is 6 internally at the moment
457 */
458class Tensor : public detail::ObjectBase<AclTensor_>
459{
460public:
461 /** Constructor
462 *
463 * @note Tensor memory is allocated
464 *
465 * @param[in] ctx Context from where the tensor will be created from
466 * @param[in] desc Tensor descriptor to be used
467 * @param[out] status Status information if requested
468 */
469 Tensor(Context &ctx, const TensorDescriptor &desc, StatusCode *status = nullptr)
470 : Tensor(ctx, desc, true, status)
471 {
472 }
473 /** Constructor
474 *
475 * @param[in] ctx Context from where the tensor will be created from
476 * @param[in] desc Tensor descriptor to be used
477 * @param[in] allocate Flag to indicate if the tensor needs to be allocated
478 * @param[out] status Status information if requested
479 */
480 Tensor(Context &ctx, const TensorDescriptor &desc, bool allocate, StatusCode *status)
481 {
482 AclTensor tensor;
483 const auto st = detail::as_enum<StatusCode>(AclCreateTensor(&tensor, ctx.get(), desc.get(), allocate));
484 reset(tensor);
485 report_status(st, "[Arm Compute Library] Failed to create tensor!");
486 if(status)
487 {
488 *status = st;
489 }
490 }
491 /** Maps the backing memory of a given tensor that can be used by the host to access any contents
492 *
493 * @return A valid non-zero pointer in case of success else nullptr
494 */
495 void *map()
496 {
497 void *handle = nullptr;
498 const auto st = detail::as_enum<StatusCode>(AclMapTensor(_object.get(), &handle));
499 report_status(st, "[Arm Compute Library] Failed to map the tensor and extract the tensor's backing memory!");
500 return handle;
501 }
502 /** Unmaps tensor's memory
503 *
504 * @param[in] handle Handle to unmap
505 *
506 * @return Status code
507 */
508 StatusCode unmap(void *handle)
509 {
510 const auto st = detail::as_enum<StatusCode>(AclUnmapTensor(_object.get(), handle));
511 report_status(st, "[Arm Compute Library] Failed to unmap the tensor!");
512 return st;
513 }
514 /** Import external memory to a given tensor object
515 *
516 * @param[in] handle External memory handle
517 * @param[in] type Type of memory to be imported
518 *
519 * @return Status code
520 */
521 StatusCode import(void *handle, ImportType type)
522 {
523 const auto st = detail::as_enum<StatusCode>(AclTensorImport(_object.get(), handle, detail::as_cenum<AclImportMemoryType>(type)));
524 report_status(st, "[Arm Compute Library] Failed to import external memory to tensor!");
525 return st;
526 }
527};
528
529/** Tensor pack class
530 *
531 * Pack is a utility construct that is used to create a collection of tensors that can then
532 * be passed into operator as inputs.
533 */
534class TensorPack : public detail::ObjectBase<AclTensorPack_>
535{
536public:
537 /** Pack pair construct */
538 struct PackPair
539 {
540 /** Constructor
541 *
542 * @param[in] tensor_ Tensor to pack
543 * @param[in] slot_id_ Slot identification of the tensor in respect with the operator
544 */
545 PackPair(Tensor *tensor_, int32_t slot_id_)
546 : tensor(tensor_), slot_id(slot_id_)
547 {
548 }
549
550 Tensor *tensor{ nullptr }; /**< Tensor object */
551 int32_t slot_id{ AclSlotUnknown }; /**< Slot id in respect with the operator */
552 };
553
554public:
555 /** Constructor
556 *
557 * @param[in] ctx Context from where the tensor pack will be created from
558 * @param[out] status Status information if requested
559 */
560 explicit TensorPack(Context &ctx, StatusCode *status = nullptr)
561 {
562 AclTensorPack pack;
563 const auto st = detail::as_enum<StatusCode>(AclCreateTensorPack(&pack, ctx.get()));
564 reset(pack);
565 report_status(st, "[Arm Compute Library] Failure during tensor pack creation");
566 if(status)
567 {
568 *status = st;
569 }
570 }
571 /** Add tensor to tensor pack
572 *
573 * @param[in] slot_id Slot id of the tensor in respect with the operator
574 * @param[in] tensor Tensor to be added in the pack
575 *
576 * @return Status code
577 */
578 StatusCode add(Tensor &tensor, int32_t slot_id)
579 {
580 return detail::as_enum<StatusCode>(AclPackTensor(_object.get(), tensor.get(), slot_id));
581 }
582 /** Add a list of tensors to a tensor pack
583 *
584 * @param[in] packed Pair packs to be added
585 *
586 * @return Status code
587 */
588 StatusCode add(std::initializer_list<PackPair> packed)
589 {
590 const size_t size = packed.size();
591 std::vector<int32_t> slots(size);
592 std::vector<AclTensor> tensors(size);
593 int i = 0;
594 for(auto &p : packed)
595 {
596 slots[i] = p.slot_id;
597 tensors[i] = AclTensor(p.tensor);
598 ++i;
599 }
600 return detail::as_enum<StatusCode>(AclPackTensors(_object.get(), tensors.data(), slots.data(), size));
601 }
602};
Georgios Pinitas8a5146f2021-01-12 15:51:07 +0000603} // namespace acl
604#undef ARM_COMPUTE_IGNORE_UNUSED
605#endif /* ARM_COMPUTE_ACL_HPP_ */