在《玩转 ArrayFire:07 数组和矩阵操作》中,我们已经了解到 ArrayFire 的数组和矩阵操作,在这一篇中,我们将继续了解 ArrayFire 的后端。
ArrayFire 的统一后端是在版本 3.2 中引入的。虽然这不是一个独立的后端,但它允许用户在运行时可以在不同的 ArrayFire 后端(CPU、CUDA和OpenCL)切换。
使用统一后端进行编译的步骤与使用任何其他后端进行编译的步骤相同。唯一的区别是可执行文件需要链接到 af 库(如 Linux 的 libaf.so、OSX的 libaf.dylib、Windows 的 af.lib 等)。如果想要与 CMake 一起使用,请使用 ArrayFire_Unified_LIBRARIES 变量。
统一后端将动态地加载后端库。后端优先级为 CUDA -> OpenCL -> CPU 。这里需要注意的最重要的一点是,ArrayFire 库所依赖的所有库都需要位于环境路径中。
如果缺少任何库,那么 ArrayFire 库将无法加载,后端也将被标记为不可用。如果该路径不在系统路径中,那么 ArrayFire 库可能会出现在 AF_PATH 或 AF_BUILD_PATH 环境变量中。这些路径被视为备用路径,以防在系统路径中找不到这些文件。但是,ArrayFire 库的所有其他上游库必须出现在上面所示的系统路径变量中。
af_backend 枚举存储可能的后端。要选择后端,调用 af::setBackend 函数,如下所示:
af::setBackend(AF_BACKEND_CUDA); // Sets CUDA as current backend
可以调用 af::getBackendCount 函数来获取后端可用的数量(成功加载 libaf* 后端库的数量)。
#include
void testBackend()
{
af::info();
af_print(af::randu(5, 4));
}
int main()
{
try {
printf("Trying CPU Backend\n");
af::setBackend(AF_BACKEND_CPU);
testBackend();
} catch (af::exception& e) {
printf("Caught exception when trying CPU backend\n");
fprintf(stderr, "%s\n", e.what());
}
try {
printf("Trying CUDA Backend\n");
af::setBackend(AF_BACKEND_CUDA);
testBackend();
} catch (af::exception& e) {
printf("Caught exception when trying CUDA backend\n");
fprintf(stderr, "%s\n", e.what());
}
try {
printf("Trying OpenCL Backend\n");
af::setBackend(AF_BACKEND_OPENCL);
testBackend();
} catch (af::exception& e) {
printf("Caught exception when trying OpenCL backend\n");
fprintf(stderr, "%s\n", e.what());
}
return 0;
}
输出结果,如下所示:
Trying CPU Backend
ArrayFire v3.2.0 (CPU, 64-bit Linux, build fc7630f)
[0] Intel: Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz Max threads(8)
af::randu(5, 4)
[5 4 1 1]
0.0000 0.2190 0.3835 0.5297
0.1315 0.0470 0.5194 0.6711
0.7556 0.6789 0.8310 0.0077
0.4587 0.6793 0.0346 0.3834
0.5328 0.9347 0.0535 0.0668
Trying CUDA Backend
ArrayFire v3.2.0 (CUDA, 64-bit Linux, build fc7630f)
Platform: CUDA Toolkit 7.5, Driver: 355.11
[0] Quadro K5000, 4093 MB, CUDA Compute 3.0
af::randu(5, 4)
[5 4 1 1]
0.7402 0.4464 0.7762 0.2920
0.9210 0.6673 0.2948 0.3194
0.0390 0.1099 0.7140 0.8109
0.9690 0.4702 0.3585 0.1541
0.9251 0.5132 0.6814 0.4452
Trying OpenCL Backend
ArrayFire v3.2.0 (OpenCL, 64-bit Linux, build fc7630f)
[0] NVIDIA : Quadro K5000
-1- INTEL : Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz
af::randu(5, 4)
[5 4 1 1]
0.4107 0.0081 0.6600 0.1046
0.8224 0.3775 0.0764 0.8827
0.9518 0.3027 0.0901 0.1647
0.1794 0.6456 0.5933 0.8060
0.4198 0.5591 0.1098 0.5938
如果不小心切换后端,很容易遇到异常。
ArrayFire 检查输入到函数的数组是否与活动后端匹配。如果在一个后端创建数组,但在另一个后端使用,则抛出代码为 503 (AF_ERR_ARR_BKND_MISMATCH)的异常。
#include
int main()
{
try {
af::setBackend(AF_BACKEND_CUDA);
af::array A = af::randu(5, 5);
af::setBackend(AF_BACKEND_OPENCL);
af::array B = af::constant(10, 5, 5);
af::array C = af::matmul(A, B); // This will throw an exception
} catch (af::exception& e) {
fprintf(stderr, "%s\n", e.what());
}
return 0;
}
我们建议您使用一种技术来跟踪后端上的数组。一种建议是在数组名中使用 _cpu, _cuda, _opencl 后缀。因此,在 CUDA 后端创建的数组将被命名为 myarray_cuda 。如果你还没有在你的代码中使用过 af::setBackend 函数,那么你就不用担心这个问题,因为所有的数组都是在相同的默认后端创建的。
不建议使用带有统一后端的自定义内核。这主要是因为统一后端意味着超级可移植,应该只使用 ArrayFire 和本地 CPU 代码。