Playing with ArrayFire: 08 unified backend


Preface

In "Playing with ArrayFire: 07 Array and Matrix Operations", we have learned about ArrayFire's array and matrix operations. In this article, we will continue to understand the backend of ArrayFire.


1. Introduction

     ArrayFire's unified backend was introduced in version 3.2. Although this is not a standalone backend, it allows users to switch between different ArrayFire backends (CPU, CUDA and OpenCL) at runtime.

Two, unified compilation

    The steps for compiling with a unified backend are the same as those for compiling with any other backend. The only difference is the need to link to the executable file af library (such as Linux, libaf.so , OSX's libaf.dylib , Windows of af.lib , etc.). If you want to use it with CMake, use the ArrayFire_Unified_LIBRARIES variable.

Three, use a unified backend

    The unified backend will dynamically load the backend library. The backend priority is CUDA -> OpenCL -> CPU . The most important thing to note here is that all libraries that the ArrayFire library depends on need to be in the environment path.

  • LD_LIBRARY_PATH -> Linux, Unix, OSX
  • DYLD_LIBRARY_PATH -> OSX
  • PATH -> Windows

    If any libraries are missing, then the ArrayFire library will fail to load and the backend will be marked as unavailable. If the path is not in the system path, the ArrayFire library may appear in the AF_PATH or AF_BUILD_PATH environment variable. These paths are considered alternate paths in case these files are not found in the system path. However, all other upstream libraries of the ArrayFire library must appear in the system path variables shown above.

Four, switch backend

     af_backend enumerates possible backends for storage. To select the backend, call the af::setBackend function, as shown below:

	af::setBackend(AF_BACKEND_CUDA);    // Sets CUDA as current backend

    You can call the af::getBackendCount function to get the number of backends available ( libaf successfully loaded * the number of backend libraries).

Five, case

	#include <arrayfire.h>
	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;
	}

     The output result is as follows:

	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

Six, matters needing attention

    If you accidentally switch the backend, it is easy to encounter exceptions.

  1. Don't use array between different backends

     ArrayFire checks whether the array input to the function matches the active backend. If an array is created in one backend, but used in another backend, an exception code of 503 ( AF_ERR_ARR_BKND_MISMATCH ) will be thrown .

	#include <arrayfire.h>
	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;
}
  1. Use a naming scheme to track array and backend

    We recommend that you use a technique to track the array on the backend. One suggestion is to use _cpu , _cuda , _opencl suffixes in the array name . Therefore, the array created on the CUDA backend will be named myarray_cuda. If you haven't used the af::setBackend function in your code , then you don't need to worry about this problem, because all arrays are created on the same default backend.

  1. Cannot use custom kernel (CUDA/OpenCL) and unified backend at the same time

    It is not recommended to use a custom kernel with a unified backend. This is mainly because a unified backend means super portable, and only ArrayFire and native CPU code should be used.


Guess you like

Origin blog.csdn.net/weixin_42467801/article/details/113739024