12

我是 C、C++ 和 OpenCL 的新手,目前正在努力学习它们。这是一个预先存在的 C++ 函数,我试图弄清楚如何使用 C 或 C++ 绑定将其移植到 OpenCL。

#include <vector>

using namespace std;

class Test {

private:

    double a;
    vector<double> b;
    vector<long> c;
    vector<vector<double> > d;

public:

    double foo(long x, double y) {
        // mathematical operations
        // using x, y, a, b, c, d
        // and also b.size()
        // to calculate return value
        return 0.0;
    }

};

从广义上讲,我的问题是如何将此函数访问的所有类成员传递到绑定和内核中。我了解如何传递标量值,但我不确定向量值。有没有办法将指针传递给上述每个成员或内存映射它们,以便 OpenCL 对它们的看法与主机内存同步?分解我的问题如下。

  1. 考虑到它们的大小可变,我如何将成员 b 和 c 传递给绑定和内核?
  2. 考虑到它是二维的,我如何传递成员 d?
  3. 我如何从内核中访问这些成员以及它们将在内核参数中声明为哪些类型?将简单地使用数组索引符号即 b[0] 进行访问吗?
  4. 我将如何在内核函数中调用等效于 b.size() 的操作,或者我不会将绑定的大小作为额外参数传递到内核中?如果改变会发生什么?

我非常感谢答案中的 C 或 C++ 绑定和内核代码示例源代码。

非常感谢。

4

1 回答 1

14
  1. 您必须分配一个 OpenCL 缓冲区并将您的 CPU 数据复制到其中。OpenCL 缓冲区具有固定大小,因此如果数据大小发生变化,您要么必须重新创建它,要么将其设置为“足够大”,如果需要更少的内存,则只使用它的一小部分。例如,要为设备创建缓冲区b并同时将其所有数据复制到设备:

    cl_mem buffer_b = clCreateBuffer(
        context, // OpenCL context
        CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, // Only read access from kernel,
                                                 // copy data from host
        sizeof(cl_double) * b.size(), // Buffer size in bytes
        &b[0], // Pointer to data to copy
        &errorcode); // Return code
    

    也可以直接映射主机内存(CL_MEM_USE_HOST_PTR),但这会对对齐和创建缓冲区后对主机内存的访问施加一些限制。基本上,当您当前未映射主机内存时,它可能包含垃圾。

  2. 这取决于。第二维向量的大小是否始终相等?然后在将它们上传到 OpenCL 设备时将它们展平。否则会变得更复杂。

  3. 您将缓冲区参数声明为__global内核中的指针。例如,__global double *b将适用于在 1 中创建的缓冲区。您可以简单地在内核中使用数组表示法来访问缓冲区中的各个元素。

  4. 您无法从内核中查询缓冲区大小,因此您必须手动传递它。这也可以隐式发生,例如,如果工作项的数量与b.

可以访问所有计算数据的内核可能如下所示:

__kernel void foo(long x, double y, double a, __global double* b, int b_size,
                  __global long* c, __global double* d,
                  __global double* result) {
  // Here be dragons
  *result = 0.0;
}

请注意,您还必须为结果分配内存。如果需要,可能需要传递额外的大小参数。您可以按如下方式调用内核:

// Create/fill buffers
// ...

// Set arguments
clSetKernelArg(kernel, 0, sizeof(cl_long), &x);
clSetKernelArg(kernel, 1, sizeof(cl_double), &y);
clSetKernelArg(kernel, 2, sizeof(cl_double), &a);
clSetKernelArg(kernel, 3, sizeof(cl_mem), &b_buffer);
cl_int b_size = b.size();
clSetKernelArg(kernel, 4, sizeof(cl_int), &b_size);
clSetKernelArg(kernel, 5, sizeof(cl_mem), &c_buffer);
clSetKernelArg(kernel, 6, sizeof(cl_mem), &d_buffer);
clSetKernelArg(kernel, 7, sizeof(cl_mem), &result_buffer);
// Enqueue kernel
clEnqueueNDRangeKernel(queue, kernel, /* ... depends on your domain */);

// Read back result
cl_double result;
clEnqueueReadBuffer(queue, result_buffer, CL_TRUE, 0, sizeof(cl_double), &result,
                    0, NULL, NULL);
于 2012-09-14T18:40:55.513 回答