Device Selection

In this section you will learn how to select a device to accelerate your compute kernels. Devices must be explicitly selected by the user; alpaka does not provide a single way to select your compute device. The reason for this is that device selection depends strongly on your application and workflow. It could be that your project only wants to use a single compute device, e.g. an NVIDIA GPU, or that you want to support using all available compute devices in your host system. You could make the device selection part of your CMake workflow, or you might want to provide command-line parameters to select the device.

To select a device you need an api and a deviceKind.

../_images/api_deviceKind.svg

Which api is available depends on the CMake flags alpaka_DEP_* or, if you use alpaka without CMake, on the compiler and selected compiler flags. Here we will show how to select a host device, which is always available and represents your CPU. If you use a combination that is not supported because the required dependency is not loaded, or because it is an invalid api and deviceKind combination, you will see a compiler error.

/* Select a device, possible combinations of api+deviceKind:
 * host+cpu, cuda+nvidiaGpu, hip+amdGpu, oneApi+intelGpu, oneApi+cpu,
 * oneApi+amdGpu, oneApi+nvidiaGpu
 */
auto computeDevSelector = alpaka::onHost::makeDeviceSelector(api::host, deviceKind::cpu);

We create an object that can allocate a device of the given device kind for us, but first we need to check if there is a device available. Maybe there is no device available due to driver issues.

auto numComputeDevs = computeDevSelector.getDeviceCount();
std::cout << "Found " << numComputeDevs << " device(s):\n";

Before we take a device let’s check the device properties e.g. the name and warp size.

// get the device properties for each device without allocating the device
for(auto i = 0u; i < numComputeDevs; ++i)
{
    std::cout << "Device " << i << ":\n";
    std::cout << "  - name              " << computeDevSelector.getDeviceProperties(i).getName() << "\n";
    std::cout << "  - #multi-processors " << computeDevSelector.getDeviceProperties(i).multiProcessorCount << "\n";
    std::cout << "  - warp size         " << computeDevSelector.getDeviceProperties(i).warpSize << "\n";
}

Calling makeDevice() using the device index to obtain the device only succeeds if the device is available, else you will get a runtime exception.

// Always check the number of available compute devices! alpaka always creates a valid DeviceSelector even for
// unsupported combinations of an api and deviceKind.
if(numComputeDevs > 0)
{
    // select the first device and get the name
    onHost::Device computeDev = computeDevSelector.makeDevice(0);
    std::cout << computeDev.getName() << "\n";
}

The device with the api host and the device kind cpu which represents your host system CPU is always available, therefore you have a shortcut interface function available.

// Get a device to perform work on the host.
// It is a shortcut compared to using the makeDeviceSelector(...) to get a host device.
onHost::concepts::Device auto hostDevice = onHost::makeHostDevice();

Complete Source File

020_device.cpp
 1/* Copyright 2025 René Widera
 2 * SPDX-License-Identifier: ISC
 3 */
 4
 5#include <alpaka/alpaka.hpp>
 6
 7#include <catch2/catch_test_macros.hpp>
 8
 9#include <iostream>
10
11using namespace alpaka;
12
13TEST_CASE("show host devices", "[docs]")
14{
15    /* Select a device, possible combinations of api+deviceKind:
16     * host+cpu, cuda+nvidiaGpu, hip+amdGpu, oneApi+intelGpu, oneApi+cpu,
17     * oneApi+amdGpu, oneApi+nvidiaGpu
18     */
19    auto computeDevSelector = alpaka::onHost::makeDeviceSelector(api::host, deviceKind::cpu);
20
21    auto numComputeDevs = computeDevSelector.getDeviceCount();
22    std::cout << "Found " << numComputeDevs << " device(s):\n";
23
24    // Always check the number of available compute devices! alpaka always creates a valid DeviceSelector even for
25    // unsupported combinations of an api and deviceKind.
26    if(numComputeDevs > 0)
27    {
28        // select the first device and get the name
29        onHost::Device computeDev = computeDevSelector.makeDevice(0);
30        std::cout << computeDev.getName() << "\n";
31    }
32
33    // get the device properties for each device without allocating the device
34    for(auto i = 0u; i < numComputeDevs; ++i)
35    {
36        std::cout << "Device " << i << ":\n";
37        std::cout << "  - name              " << computeDevSelector.getDeviceProperties(i).getName() << "\n";
38        std::cout << "  - #multi-processors " << computeDevSelector.getDeviceProperties(i).multiProcessorCount << "\n";
39        std::cout << "  - warp size         " << computeDevSelector.getDeviceProperties(i).warpSize << "\n";
40    }
41}
42
43TEST_CASE("host device", "[docs]")
44{
45    // Get a device to perform work on the host.
46    // It is a shortcut compared to using the makeDeviceSelector(...) to get a host device.
47    onHost::concepts::Device auto hostDevice = onHost::makeHostDevice();
48
49    // Getting a queue to enqueue asynchronous work
50    onHost::Queue hostQueue = hostDevice.makeQueue();
51    hostQueue.enqueueHostFn([]() { std::cout << "Hallo host task" << std::endl; });
52    onHost::wait(hostQueue);
53}