many workers on GPU

如何在 Intel GPU 上跑 OpenCL?

首先,我們得先去裝 Intel 的 Compute Runtime 來裝驅動程式,直接用 apt install 裝的可能會太舊。

裝完之後可以順邊裝一下 sudo apt install clinfo,然後使用 clinfo 指令看一下 OpenCL 是不是可以真的抓到 GPU 資訊。

雖然 OpenCL 可以執行時直接讀取 .cl 檔案來進行編譯 Kernel,但我們希望事先編譯好 Kernel,也就是使用 AOT 技術,這樣執行時才不用重新編譯,因此我們會需要使用 ocloc 編譯器事先編譯。根據平台我們要下的 -device 參數不一樣,可以在 Intel 文件中「Use AOT for Integrated Graphics」找到對應的代碼。

例如我使用的 Intel HD Graphics P530 其代碼就是 glk

以下以 CMake 作為範例,CMakeList.txt 中我們使用以下程式碼:

# 找到 OpenCL
find_package(OpenCL REQUIRED)

# 找到 ocloc
set(OCLOC_EXECUTABLE "/usr/bin/ocloc")

# 使用 ocloc 編譯 kernel 檔案
execute_process(
    COMMAND ${OCLOC_EXECUTABLE} -device glk -file ${KERNEL_SOURCE} -output kernel.bin
    OUTPUT_VARIABLE OCLOC_OUTPUT
    ERROR_VARIABLE OCLOC_ERROR
    RESULT_VARIABLE OCLOC_RESULT
    WORKING_DIRECTORY /tmp
)

# 安裝檔案到指定位置
install(FILES "/tmp/kernel.bin_glk.bin" DESTINATION /somewhere/path/kernel.bin_glk.bin)

題外話,即使設置 -output kernel.bin,ocloc 編譯完還是會自動加後綴 bin_glk 其實有點惱人

這樣之後寫 OpenCL 程式的時候,就可以直接去 /somewhere/path/kernel.bin_glk.bin 讀取編譯好的 Kernel Binary 檔案。

讀取 Binary 的部份是這樣寫:

#define CL_HPP_TARGET_OPENCL_VERSION 300 // use OpenCL 3.0
#define CL_HPP_ENABLE_EXCEPTIONS

#include <CL/opencl.hpp>

cl::Device device;          
cl::Program program;         
cl::Context context;               
cl::CommandQueue queue;          
std::vector<int> binaryStatus;
cl_int clError = 0;

std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);

cl::Platform& platform = platforms.front();
std::vector<cl::Device> devices;
platform.getDevices(CL_DEVICE_TYPE_ALL, &devices);

if (devices.empty()) {
   std::cerr << "no device found" << std::endl;
}

device = devices.front();
context = cl::Context(device);
queue = cl::CommandQueue(context, device, CL_QUEUE_PROFILING_ENABLE);

// 讀取 OpenCL Binary
std::ifstream binaryFile(binaryFilePath, std::ios::binary);
if (!binaryFile.is_open()) {
    std::cerr << "cannot open file" << std::endl;
}
const std::vector<uint8_t> binaryBuffer(std::istreambuf_iterator<char>(binaryFile), {});
binaryFile.close();

cl::Program::Binaries binaries;
binaries.push_back(binaryBuffer);

// 使用 Binary 建立 cl::Program
program = cl::Program(context, {device}, binaries, &binaryStatus, &clError);

program.build();
if (clError != CL_SUCCESS) {
    std::cerr << program.getBuildInfo<CLprogram_BUILD_STATUS>(device) << std::endl;
    std::cerr << program.getBuildInfo<CLprogram_BUILD_LOG>(device) << std::endl;
}

// 一般的 OpenCL 寫法 ...

剩下的部分就是一般的 OpenCL 程式設計,本文就不多介紹了。