c++arrayfire

Usage of unified backend in ArrayFire


I'm trying to select my arrayfire backend based on the available backends and their capability (if the backend supports doubles, then use it, else not). Therefore, I created a CMakeLists.txt-file:

cmake_minimum_required(VERSION 3.10)

project(arrayfire_test)

if(USE_EXTERNAL_PATHS)
    set(CMAKE_C_COMPILER ${C_COMPILER})# CACHE PATH "" FORCE)
    set(CMAKE_CXX_COMPILER ${CXX_COMPILER})#CACHE PATH "" FORCE)
else()
    set(CMAKE_C_COMPILER mpicc)
    set(CMAKE_CXX_COMPILER mpic++)
endif()

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    set(CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -fopenmp-simd")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
    set(CXX_FLAGS "${CMAKE_CXX_FLAGS} -xHost")
endif()

set(C_FLAGS "${CMAKE_C_FLAGS} ${CXX_FLAGS} -fPIC -flto -march=native -fopenmp -O2 -funroll-loops -funroll-all-loops -fstrict-aliasing  -std=gnu++14")
set(CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_FLAGS} -fPIC -flto -march=native -fopenmp -O2 -funroll-loops -funroll-all-loops -fstrict-aliasing  -std=gnu++14")

string(REPLACE " " ";" REPLACED_CXX_FLAGS ${CXX_FLAGS})

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)

set(ArrayFire_DIR "/opt/arrayfire/share/ArrayFire/cmake")
FIND_PACKAGE(ArrayFire)

find_package(Armadillo REQUIRED PATHS "/opt/armadillo")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp -std=c++11 -O0 -g3 -flto -march=native")
set(FORGE_LIBRARIES  "/opt/arrayfire/lib/libforge.so")

INCLUDE_DIRECTORIES(${ArrayFire_INCLUDE_DIRS} 
    ${ARMADILLO_INCLUDE_DIRS})

add_executable(${PROJECT_NAME} "source/main.cpp")

target_compile_options(${PROJECT_NAME} PRIVATE ${REPLACED_CXX_FLAGS})

TARGET_LINK_LIBRARIES(${PROJECT_NAME} 
    ${ArrayFire_Unified_LIBRARIES}
    ${ARMADILLO_LIBRARIES})# ${FORGE_LIBRARIES})

and the main-file:

int main(void){
    int backends = af::getAvailableBackends ();
    std::cout << backends << "\n";
    std::vector<af_backend> possible_backends;
    if(backends & AF_BACKEND_CUDA)
        possible_backends.push_back(AF_BACKEND_CUDA);
    if(backends & AF_BACKEND_OPENCL)
        possible_backends.push_back(AF_BACKEND_OPENCL);
    if(backends & AF_BACKEND_CPU)
        possible_backends.push_back(AF_BACKEND_CPU);
    if(possible_backends.size() == 0){
        std::cerr << "No backends available\n";
        return -1;
    }
    for(size_t i = 0; i < possible_backends.size(); ++i){
        af::setBackend (possible_backends[i]);
        af::setDevice (0);
        if(af::isDoubleAvailable (0)){
            std::cout << "Backend " << af::getActiveBackend () << " supports double\n";
            break;
        }
    }
    std::cout << "Backend " << af::getActiveBackend () << " is in use\n";
    return 0;
}

According to http://arrayfire.org/docs/unifiedbackend.htm, I have to use ${ArrayFire_Unified_LIBRARIES} when using this backend. But when running the code, I get

0
No backends available

even though I have both CPU and OpenCL as backend. When replacing ${ArrayFire_Unified_LIBRARIES} with ${ArrayFire_CPU_LIBRARIES} ${ArrayFire_OpenCL_LIBRARIES}, I get the output

1
Backend 1 supports double
Backend 1 is in use

When switching the order of the libraries, I get

4
Backend 4 is in use

and thereby indicating that only one backend can be seen, even though I have two possible backends. Thus, is there a bug in my code, or is my approach wrong?


Solution

  • Firstly, We recommend using import targets instead of the raw cmake variables while integrating ArrayFire in your cmake based project. Since your minimum cmake version is 3.10, you shouldn't have any issues using import targets.

    find_package(ArrayFire)
    add_executable(<my_executable> [list your source files here])
    
    # To use Unified backend, do the following.
    # Unified backend lets you choose the backend at runtime
    target_link_libraries(<my_executable> ArrayFire::af)
    # for directly using cpu/cuda/opencl backend
    # use `ArrayFire::[afcpu | afcuda | afopencl]` respectively
    

    You don't need to link against forge, it will be loaded at runtime by ArrayFire and graphics functionality will be auto-enabled.

    For any runtime library(forge, freeimage) that is loaded by ArrayFire and unified backend to work correctly, the path of the location where these libraries are present (usually /opt/arrayfire/lib) should be visible for runtime loading. Please go through the installing tutorial to verify if you have completed all the steps required.