Skip to content

Commit b68c968

Browse files
Mark Poscabloumar456
authored andcommitted
Add functions to specify af library path to use in unified backend
1 parent 20982df commit b68c968

11 files changed

Lines changed: 914 additions & 79 deletions

File tree

docs/details/backend.dox

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,78 @@ will return in an exception.
1616

1717
=======================================================================
1818

19+
\defgroup unified_func_addbackendlibrary af_add_backend_library
20+
21+
\brief Adds a new backend library from an arbitrary path on the file system
22+
23+
This function simply registers a new backend library located anywhere in the
24+
file system, given the path of that library (can be a `.so`, `.dll`, or
25+
`.dylib` file, depending on the operating system being used).
26+
27+
Once registered, \ref af_set_backend_library() can be called to activate the
28+
backend library in a program. The order in which a library is added determines
29+
the index to be passed in \ref af_set_backend_library() to activate the desired
30+
library. The number of libraries that can be registered and activated is limited
31+
to 7.
32+
33+
This function, together with \ref af_set_backend_library() can be used to load
34+
different versions of the ArrayFire library at runtime.
35+
36+
This is a noop when using any one of the standalone CPU, CUDA, or OpenCL
37+
backends.
38+
39+
The following is an example of how this can be used:
40+
\code
41+
af_add_backend_library("/path/to/custom/1/afcpu.so");
42+
af_add_backend_library("/path/to/custom/2/afcpu.so");
43+
44+
af_set_backend_library(0); // Activates .../1/afcpu.so
45+
af_set_backend_library(1); // Activates .../2/afcpu.so
46+
\endcode
47+
48+
\ingroup unified_func
49+
\ingroup arrayfire_func
50+
51+
=======================================================================
52+
53+
\defgroup unified_func_setbackendlibrary af_set_backend_library
54+
55+
\brief Activates an ArrayFire library located in an arbitrary path for use
56+
57+
This function is used after the ArrayFire library has been registered through
58+
\ref af_add_backend_library(). The index to be passed in must be the order of
59+
when \ref af_add_backend_library() was called for the target library. This is
60+
can be used to load different versions of the ArrayFire library at runtime.
61+
62+
This is a noop when using any one of the standalone CPU, CUDA, or OpenCL backends.
63+
64+
The following is an example of how this can be used:
65+
\code
66+
af_add_backend_library("/path/to/custom/1/afcpu.so");
67+
af_add_backend_library("/path/to/custom/2/afcpu.so");
68+
69+
af_set_backend_library(0); // Activates .../1/afcpu.so
70+
af_set_backend_library(1); // Activates .../2/afcpu.so
71+
\endcode
72+
73+
\ingroup unified_func
74+
\ingroup arrayfire_func
75+
76+
=======================================================================
77+
78+
\defgroup unified_func_checkunifiedbackend af_check_unified_backend
79+
80+
\brief Returns `true` if the unified backend is used to call ArrayFire functions
81+
82+
This function returns `true` if the unified backend is being used to call the
83+
ArrayFire functions. If the binary is linked directly to one of the backend
84+
libraries, this function returns `false`.
85+
86+
\ingroup unified_func
87+
\ingroup arrayfire_func
88+
89+
=======================================================================
90+
1991
\defgroup unified_func_getbackendcount getBackendCount
2092

2193
\brief Get the number of backends whose libraries were successfully loaded.

include/af/backend.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,46 @@ extern "C" {
2424
AFAPI af_err af_set_backend(const af_backend bknd);
2525
#endif
2626

27+
#if AF_API_VERSION >= 39
28+
/**
29+
Adds a new backend library from an arbitrary path on the file system
30+
31+
\param[in] lib_path is the path of the custom arrayfire library to
32+
be registered
33+
\returns \ref af_err error code
34+
35+
\ingroup unified_func_addbackendlibrary
36+
*/
37+
AFAPI af_err af_add_backend_library(const char *lib_path);
38+
#endif
39+
40+
#if AF_API_VERSION >= 39
41+
/**
42+
Activates an ArrayFire library located in an arbitrary path for use
43+
44+
\param[in] lib_idx is the index of a registered custom arrayfire library to
45+
be activated for use. Must match the order of when the library
46+
was registered
47+
\returns \ref af_err error code
48+
49+
\ingroup unified_func_setbackendlibrary
50+
*/
51+
AFAPI af_err af_set_backend_library(int lib_idx);
52+
#endif
53+
54+
#if AF_API_VERSION >= 39
55+
/**
56+
Returns true if the unified backend is used to call ArrayFire functions
57+
58+
\param[out] result reports whether the application is using the unified
59+
backend or not
60+
\returns \ref af_err error code
61+
62+
\ingroup unified_func_checkunifiedbackend
63+
*/
64+
AFAPI af_err af_check_unified_backend(bool *result);
65+
#endif
66+
2767
#if AF_API_VERSION >= 32
2868
/**
2969
\param[out] num_backends Number of available backends

include/af/defines.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,48 @@ typedef enum {
193193
, AF_ERR_ARR_BKND_MISMATCH = 503
194194
#endif
195195

196+
#if AF_API_VERSION >= 39
197+
///
198+
/// No active backend
199+
///
200+
, AF_ERR_NO_ACTIVE_BKND = 504
201+
#endif
202+
203+
#if AF_API_VERSION >= 39
204+
///
205+
/// No available devices for this backend
206+
///
207+
, AF_ERR_BKND_NO_DEVICE = 505
208+
#endif
209+
210+
#if AF_API_VERSION >= 39
211+
///
212+
/// Target backend library not available
213+
///
214+
, AF_ERR_NO_TGT_BKND_LIB = 507
215+
#endif
216+
217+
#if AF_API_VERSION >= 39
218+
///
219+
/// Loaded backend library is invalid
220+
///
221+
, AF_ERR_BKND_LIB_INVALID = 508
222+
#endif
223+
224+
#if AF_API_VERSION >= 39
225+
///
226+
/// Backend library list full
227+
///
228+
, AF_ERR_BKND_LIB_LIST_FULL = 509
229+
#endif
230+
231+
#if AF_API_VERSION >= 39
232+
///
233+
/// Backend library index out of bounds
234+
///
235+
, AF_ERR_BKND_LIB_IDX_INVALID = 510
236+
#endif
237+
196238
// 900-999 Errors from upstream libraries and runtimes
197239

198240
///

src/api/c/device.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ af_err af_set_backend(const af_backend bknd) {
5959
return AF_SUCCESS;
6060
}
6161

62+
af_err af_add_backend_library(const char* lib_path) { return AF_SUCCESS; }
63+
64+
af_err af_set_backend_library(int lib_idx) { return AF_SUCCESS; }
65+
66+
af_err af_check_unified_backend(bool* result) {
67+
*result = false;
68+
return AF_SUCCESS;
69+
}
70+
6271
af_err af_get_backend_count(unsigned* num_backends) {
6372
*num_backends = 1;
6473
return AF_SUCCESS;

src/api/unified/device.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ af_err af_set_backend(const af_backend bknd) {
1717
return unified::setBackend(bknd);
1818
}
1919

20+
af_err af_add_backend_library(const char *lib_path) {
21+
return unified::AFSymbolManager::getInstance().addBackendLibrary(lib_path);
22+
}
23+
24+
af_err af_set_backend_library(int lib_idx) {
25+
return unified::AFSymbolManager::getInstance().setBackendLibrary(lib_idx);
26+
}
27+
28+
af_err af_check_unified_backend(bool *result) {
29+
*result = true;
30+
return AF_SUCCESS;
31+
}
32+
2033
af_err af_get_backend_count(unsigned *num_backends) {
2134
*num_backends = unified::AFSymbolManager::getInstance().getBackendCount();
2235
return AF_SUCCESS;

src/api/unified/symbol_manager.cpp

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,19 @@ LibHandle& getActiveHandle() {
179179
return activeHandle;
180180
}
181181

182+
LibHandle& getPreviousHandle() {
183+
thread_local LibHandle previousHandle =
184+
AFSymbolManager::getInstance().getDefaultHandle();
185+
return previousHandle;
186+
}
187+
182188
AFSymbolManager::AFSymbolManager()
183-
: defaultHandle(nullptr)
189+
: bkndHandles{}
190+
, defaultHandle(nullptr)
184191
, numBackends(0)
192+
, newCustomHandleIndex(NUM_BACKENDS)
185193
, backendsAvailable(0)
194+
, defaultBackend(AF_BACKEND_DEFAULT)
186195
, logger(loggerFactory("unified")) {
187196
// In order of priority.
188197
static const af_backend order[] = {AF_BACKEND_CUDA, AF_BACKEND_OPENCL,
@@ -229,20 +238,87 @@ af_err setBackend(af::Backend bknd) {
229238
auto& instance = AFSymbolManager::getInstance();
230239
if (bknd == AF_BACKEND_DEFAULT) {
231240
if (instance.getDefaultHandle()) {
232-
getActiveHandle() = instance.getDefaultHandle();
233-
getActiveBackend() = instance.getDefaultBackend();
241+
getPreviousHandle() = getActiveHandle();
242+
getActiveHandle() = instance.getDefaultHandle();
243+
getActiveBackend() = instance.getDefaultBackend();
234244
return AF_SUCCESS;
235245
} else {
236-
UNIFIED_ERROR_LOAD_LIB();
246+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_NO_TGT_BKND_LIB);
237247
}
238248
}
239249
int idx = bknd >> 1U; // Convert 1, 2, 4 -> 0, 1, 2
240250
if (instance.getHandle(idx)) {
241-
getActiveHandle() = instance.getHandle(idx);
251+
getPreviousHandle() = getActiveHandle();
252+
getActiveHandle() = instance.getHandle(idx);
253+
getActiveBackend() = bknd;
254+
return AF_SUCCESS;
255+
} else {
256+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_NO_TGT_BKND_LIB);
257+
}
258+
}
259+
260+
af_err AFSymbolManager::addBackendLibrary(const char* lib_path) {
261+
if ((newCustomHandleIndex + 1) > MAX_BKND_HANDLES) {
262+
// No more space for an additional handle
263+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_BKND_LIB_LIST_FULL);
264+
}
265+
266+
string show_flag = getEnvVar("AF_SHOW_LOAD_PATH");
267+
bool show_load_path = show_flag == "1";
268+
269+
typedef af_err (*func)(int*);
270+
LibHandle handle = nullptr;
271+
if ((handle = loadLibrary(lib_path))) {
272+
func count_func =
273+
(func)getFunctionPointer(handle, "af_get_device_count");
274+
if (count_func) {
275+
int count = 0;
276+
count_func(&count);
277+
AF_TRACE("Device Count: {}.", count);
278+
if (count == 0) {
279+
// No available device for this backend
280+
handle = nullptr;
281+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_BKND_NO_DEVICE);
282+
}
283+
} else {
284+
// Loaded library is invalid
285+
handle = nullptr;
286+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_BKND_LIB_INVALID);
287+
}
288+
289+
if (show_load_path) { printf("Using %s\n", lib_path); }
290+
291+
bkndHandles[newCustomHandleIndex] = handle;
292+
newCustomHandleIndex++;
293+
294+
return AF_SUCCESS;
295+
} else {
296+
// loadLibrary failed, maybe because path is invalid or another reason
297+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_LOAD_LIB);
298+
}
299+
}
300+
301+
af_err AFSymbolManager::setBackendLibrary(int lib_idx) {
302+
typedef af_err (*func)(af_backend*);
303+
int actual_idx = lib_idx + NUM_BACKENDS;
304+
305+
if (actual_idx >= MAX_BKND_HANDLES) {
306+
// lib_idx more than the capacity of bkndHandles
307+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_BKND_LIB_IDX_INVALID);
308+
}
309+
310+
if (bkndHandles[actual_idx]) {
311+
getPreviousHandle() = getActiveHandle();
312+
getActiveHandle() = getHandle(actual_idx);
313+
af_backend bknd = (af_backend)0;
314+
func get_backend_func = (func)getFunctionPointer(
315+
getActiveHandle(), "af_get_active_backend");
316+
if (get_backend_func) { get_backend_func(&bknd); }
242317
getActiveBackend() = bknd;
243318
return AF_SUCCESS;
244319
} else {
245-
UNIFIED_ERROR_LOAD_LIB();
320+
// lib_idx not pointing to a library yet
321+
UNIFIED_ERROR_LOAD_LIB(AF_ERR_NO_TGT_BKND_LIB);
246322
}
247323
}
248324

src/api/unified/symbol_manager.hpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@
2323

2424
namespace unified {
2525

26-
const int NUM_BACKENDS = 3;
26+
const int NUM_BACKENDS = 3;
27+
const int MAX_BKND_HANDLES = 10;
2728

28-
#define UNIFIED_ERROR_LOAD_LIB() \
29+
#define UNIFIED_ERROR_LOAD_LIB(AF_ERR) \
2930
AF_RETURN_ERROR( \
3031
"Failed to load dynamic library. " \
3132
"See http://www.arrayfire.com/docs/unifiedbackend.htm " \
3233
"for instructions to set up environment for Unified backend.", \
33-
AF_ERR_LOAD_LIB)
34+
AF_ERR)
3435

3536
static inline int backend_index(af::Backend be) {
3637
switch (be) {
@@ -55,6 +56,9 @@ class AFSymbolManager {
5556
af::Backend getDefaultBackend() { return defaultBackend; }
5657
LibHandle getDefaultHandle() { return defaultHandle; }
5758

59+
af_err addBackendLibrary(const char* lib_path);
60+
af_err setBackendLibrary(int lib_idx);
61+
5862
spdlog::logger* getLogger();
5963
LibHandle getHandle(int idx) { return bkndHandles[idx]; }
6064

@@ -69,10 +73,11 @@ class AFSymbolManager {
6973
void operator=(AFSymbolManager const&);
7074

7175
private:
72-
LibHandle bkndHandles[NUM_BACKENDS]{};
76+
LibHandle bkndHandles[MAX_BKND_HANDLES]{};
7377

7478
LibHandle defaultHandle;
7579
unsigned numBackends;
80+
unsigned newCustomHandleIndex;
7681
int backendsAvailable;
7782
af_backend defaultBackend;
7883
std::shared_ptr<spdlog::logger> logger;
@@ -84,6 +89,8 @@ af::Backend& getActiveBackend();
8489

8590
LibHandle& getActiveHandle();
8691

92+
LibHandle& getPreviousHandle();
93+
8794
namespace {
8895
bool checkArray(af_backend activeBackend, const af_array a) {
8996
// Convert af_array into int to retrieve the backend info.
@@ -144,7 +151,8 @@ bool checkArrays(af_backend activeBackend, T a, Args... arg) {
144151
if (unified::getActiveHandle()) { \
145152
thread_local af_func func = (af_func)common::getFunctionPointer( \
146153
unified::getActiveHandle(), __func__); \
147-
if (!func) { \
154+
if (!func && \
155+
unified::getActiveHandle() != unified::getPreviousHandle()) { \
148156
AF_RETURN_ERROR( \
149157
"requested symbol name could not be found in loaded library.", \
150158
AF_ERR_LOAD_LIB); \
@@ -156,8 +164,8 @@ bool checkArrays(af_backend activeBackend, T a, Args... arg) {
156164
} \
157165
return func(__VA_ARGS__); \
158166
} else { \
159-
AF_RETURN_ERROR("ArrayFire couldn't locate any backends.", \
160-
AF_ERR_LOAD_LIB); \
167+
AF_RETURN_ERROR("ArrayFire couldn't load active backend", \
168+
AF_ERR_NO_ACTIVE_BKND); \
161169
}
162170

163171
#define CALL_NO_PARAMS(FUNCTION) CALL(FUNCTION)

0 commit comments

Comments
 (0)