$darkmode
A high-performance general-purpose compute library
/home/abuild/rpmbuild/BUILD/arrayfire-full-v3.9.0/docs/pages/unified_backend.md
Go to the documentation of this file.
1 Unified Backend {#unifiedbackend}
2 ==========
3 
4 [TOC]
5 
6 # Introduction
7 
8 The Unified backend was introduced in ArrayFire with version 3.2.
9 While this is not an independent backend, it allows the user to switch between
10 the different ArrayFire backends (CPU, CUDA, oneAPI and OpenCL) at runtime.
11 
12 # Compiling with Unified
13 
14 The steps to compile with the unified backend are the same as compiling with
15 any of the other backends.
16 The only change being that the executable needs to be linked with the __af__
17 library (`libaf.so` (Linux), `libaf.dylib` (OSX), `af.lib` (Windows)).
18 
19 Check the Using with [Linux](\ref using_on_linux), [OSX](\ref using_on_osx),
20 [Windows](\ref using_on_windows) for more details.
21 
22 To use with CMake, use the __ArrayFire_Unified_LIBRARIES__ variable.
23 
24 # Using the Unified Backend
25 
26 The Unified backend will try to dynamically load the backend libraries. The
27 priority of backends is __CUDA -> oneAPI -> OpenCL -> CPU__
28 
29 The most important aspect to note here is that all the libraries the ArrayFire
30 libs depend on need to be in the environment paths
31 
32 * `LD_LIBRARY_PATH` -> Linux, Unix, OSX
33 * `DYLD_LIBRARY_PATH` -> OSX
34 * `PATH` -> Windows
35 
36 If any of the libs are missing, then the library will fail to load and the
37 backend will be marked as unavailable.
38 
39 Optionally, The ArrayFire libs may be present in `AF_PATH` or `AF_BUILD_PATH`
40 environment variables if the path is not in the system paths. These are
41 treated as fallback paths in case the files are not found in the system paths.
42 However, all the other upstream libraries for ArrayFire libs must be present
43 in the system path variables shown above.
44 
45 # Switching Backends
46 
47 The af_backend enum stores the possible backends.
48 To select a backend, call the af::setBackend function as shown below.
49 
50 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
51 af::setBackend(AF_BACKEND_CUDA); // Sets CUDA as current backend
52 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
53 
54 To get the count of the number of backends available (the number of `libaf*`
55 backend libraries loaded successfully), call the af::getBackendCount function.
56 
57 # Example
58 
59 This example is shortened form of [basic.cpp](\ref unified/basic.cpp).
60 
61 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
62 #include <arrayfire.h>
63 
64 void testBackend()
65 {
66  af::info();
67  af_print(af::randu(5, 4));
68 }
69 
70 int main()
71 {
72  try {
73  printf("Trying CPU Backend\n");
74  af::setBackend(AF_BACKEND_CPU);
75  testBackend();
76  } catch (af::exception& e) {
77  printf("Caught exception when trying CPU backend\n");
78  fprintf(stderr, "%s\n", e.what());
79  }
80 
81  try {
82  printf("Trying oneAPI Backend\n");
83  af::setBackend(AF_BACKEND_ONEAPI);
84  testBackend();
85  } catch (af::exception& e) {
86  printf("Caught exception when trying oneAPI backend\n");
87  fprintf(stderr, "%s\n", e.what());
88  }
89 
90  try {
91  printf("Trying CUDA Backend\n");
92  af::setBackend(AF_BACKEND_CUDA);
93  testBackend();
94  } catch (af::exception& e) {
95  printf("Caught exception when trying CUDA backend\n");
96  fprintf(stderr, "%s\n", e.what());
97  }
98 
99  try {
100  printf("Trying OpenCL Backend\n");
101  af::setBackend(AF_BACKEND_OPENCL);
102  testBackend();
103  } catch (af::exception& e) {
104  printf("Caught exception when trying OpenCL backend\n");
105  fprintf(stderr, "%s\n", e.what());
106  }
107 
108  return 0;
109 }
110 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
111 
112 This output would be:
113 
114  Trying CPU Backend
115  ArrayFire v3.9.0 (CPU, 64-bit Linux, build 23ee0650e)
116  [0] AMD: AMD Ryzen Threadripper PRO 3955WX 16-Cores af::randu(5, 4)
117  [5 4 1 1]
118  0.6010 0.5497 0.1583 0.3636
119  0.0278 0.2864 0.3712 0.4165
120  0.9806 0.3410 0.3543 0.5814
121  0.2126 0.7509 0.6450 0.8962
122  0.0655 0.4105 0.9675 0.3712
123 
124  Trying oneAPI Backend
125  ArrayFire v3.9.0 (oneAPI, 64-bit Linux, build 23ee0650e)
126  [0] Intel(R) OpenCL: AMD Ryzen Threadripper PRO 3955WX 16-Cores , 128650 MB (fp64)
127  af::randu(5, 4)
128  [5 4 1 1]
129  0.6010 0.5497 0.1583 0.3636
130  0.0278 0.2864 0.3712 0.4165
131  0.9806 0.3410 0.3543 0.5814
132  0.2126 0.7509 0.6450 0.8962
133  0.0655 0.4105 0.9675 0.3712
134 
135  Trying CUDA Backend
136  ArrayFire v3.9.0 (CUDA, 64-bit Linux, build 23ee0650e)
137  Platform: CUDA Runtime 12.2, Driver: 535.104.05
138  [0] NVIDIA RTX A5500, 22721 MB, CUDA Compute 8.6
139  -1- NVIDIA RTX A5500, 22719 MB, CUDA Compute 8.6
140  af::randu(5, 4)
141  [5 4 1 1]
142  0.6010 0.5497 0.1583 0.3636
143  0.0278 0.2864 0.3712 0.4165
144  0.9806 0.3410 0.3543 0.5814
145  0.2126 0.7509 0.6450 0.8962
146  0.0655 0.4105 0.9675 0.3712
147 
148  Trying OpenCL Backend
149  ArrayFire v3.9.0 (OpenCL, 64-bit Linux, build 23ee0650e)
150  [0] NVIDIA: NVIDIA RTX A5500, 22720 MB
151  -1- NVIDIA: NVIDIA RTX A5500, 22718 MB
152  -2- Intel(R) FPGA Emulation Platform for OpenCL(TM): Intel(R) FPGA Emulation Device, 128650 MB
153  -3- INTEL: AMD Ryzen Threadripper PRO 3955WX 16-Cores , 128650 MB
154  af::randu(5, 4)
155  [5 4 1 1]
156  0.6010 0.5497 0.1583 0.3636
157  0.0278 0.2864 0.3712 0.4165
158  0.9806 0.3410 0.3543 0.5814
159  0.2126 0.7509 0.6450 0.8962
160  0.0655 0.4105 0.9675 0.3712
161 
162 
163 # Dos and Don'ts
164 
165 It is very easy to run into exceptions if you are not careful with the
166 switching of backends.
167 
168 ### Don't: Do not use arrays between different backends
169 
170 ArrayFire checks the input arrays to functions for mismatches with the active
171 backend. If an array created on one backend, but used when another backend is
172 set to active, an exception with code 503 (`AF_ERR_ARR_BKND_MISMATCH`) is
173 thrown.
174 
175 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
176 #include <arrayfire.h>
177 
178 int main()
179 {
180  try {
181  af::setBackend(AF_BACKEND_CUDA);
182  af::array A = af::randu(5, 5);
183 
184  af::setBackend(AF_BACKEND_OPENCL);
185  af::array B = af::constant(10, 5, 5);
186  af::array C = af::matmul(A, B); // This will throw an exception
187 
188  } catch (af::exception& e) {
189  fprintf(stderr, "%s\n", e.what());
190  }
191 
192  return 0;
193 }
194 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195 
196 ### Do: Use a naming scheme to track arrays and backends
197 
198 We recommend that you use a technique to track the arrays on the backends. One
199 suggested technique would be to use a suffix of `_cpu`, `_cuda`, `_opencl`
200 with the array names. So an array created on the CUDA backend would be named
201 `myarray_cuda`.
202 
203 If you have not used the af::setBackend function anywhere in your code, then
204 you do not have to worry about this as all the arrays will be created on the
205 same default backend.
206 
207 ### Don't: Do not use custom kernels (CUDA/OpenCL) with the Unified backend
208 
209 This is another area that is a no go when using the Unified backend. It not
210 recommended that you use custom kernels with unified backend. This is mainly
211 becuase the Unified backend is meant to be ultra portable and should use only
212 ArrayFire and native CPU code.