OpenCL 从内核发送数据到主机

OpenCL 从内核发送数据到主机

问题描述

我试图制作一个内核,它偶尔会更新主机关于其进度的信息,因为该内核执行一个耗时的计算任务(需要几小时甚至几天才能完成)。然而,除了使用 printf 之外,我还没有找到一种从内核到主机的通信方式,这让我相信这种通信在理论上是可行的。

到目前为止,我尝试使用一个缓冲区,主机会定期检查是否有新的消息,但对于我的Nvidia GPU来说,在内核仍在执行时访问GPU内存的任何方式都是被锁定的。我尝试过使用共享内存,以及映射一个缓冲区,但都没有成功。

我也知道在OpenCL中存在事件,但我不确定如何在主机上创建一个用户事件并将其传递给内核。

我陷入了一个死胡同,任何帮助都将受到赞赏。我对OpenCL没有太多经验,所以我对大多数OpenCL的结构和概念不太了解。

这是我尝试过的代码,使用缓冲区:

#include <cstdio>
#include <CL/opencl.hpp>
#include <chrono>
#include <thread>

#define WORKER_COUNT 1024

struct packet_t {
    int completed_count;
    bool pending;
};

bool running = true;
cl_int err;
const char *kernel_code = "\
struct packet_t {\
    int completed_count;\
    bool pending;\
};\
__kernel void run(volatile __global struct packet_t *packet) {\
    size_t tid = get_global_id(0);\
    packet[tid].completed_count = 10;\
    packet[tid].pending = true;\
}";

void handle() {
    if (err != CL_SUCCESS) {
        fprintf(stderr, "Error %d!\n", err);
        exit(err);
    }
}

int main() {
    cl::Platform platform = cl::Platform::get(&err); handle(); // handle will check for errors
    cl_context_properties cprops[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)platform(), 0 };
    cl::Context context { CL_DEVICE_TYPE_ALL, cprops, NULL, NULL, &err }; handle();
    cl::CommandQueue queue(context, context.getInfo<CL_CONTEXT_DEVICES>()[0], 0, &err); handle();
    cl::Event event;

    cl::Buffer packetsBuff { context, CL_MEM_ALLOC_HOST_PTR, sizeof(packet_t) * WORKER_COUNT, NULL, &err }; handle();
    volatile packet_t *packets = (packet_t*)queue.enqueueMapBuffer(packetsBuff, true, CL_MAP_READ, 0, sizeof(packet_t) * WORKER_COUNT, NULL, NULL, &err);

    cl::Program program { context, kernel_code };
    err = program.build(); handle();

    cl::Kernel kernel { program, "run", &err }; handle();
    err = kernel.setArg(0, packetsBuff); handle();
    err = queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(WORKER_COUNT), cl::NDRange(128), NULL, &event);
    handle();

    event.setCallback(CL_COMPLETE, [](cl_event e, cl_int i, void *v) { running = false; });

    while (running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        for (size_t i = 0; i < WORKER_COUNT; i++) {
            if (packets[i].pending) {
                printf("Worker %d has done %d stuff.\n", packets[i].completed_count);
            }
        }
    }
}

解决方案

除非你有充分的理由以更复杂的方式进行操作,否则我强烈建议您只让内核运行一个很短的时间,比如100毫秒,然后将它所需要的任何内容保存到全局内存中。与局部内存或私有内存不同,全局内存在内核调用之间始终保持不变。我的意思是让内核按照以下方式工作。

A. Load state from global buffer
B. Do some work
C. Save state to global buffer

然后您的主机可以通过循环读取内核调用之间的缓冲区。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程