Как передать и получить доступ к векторам С++ для ядра OpenCL?

Я новичок в C, С++ и OpenCL и делаю все возможное, чтобы изучить их на данный момент. Здесь существовала ранее существовавшая функция С++, которую я пытаюсь выяснить, как подключиться к OpenCL с помощью привязок C или С++.

#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 их синхронизировалось с памятью хоста? Сломанные мои вопросы таковы, как показано ниже.

  • Как передать члену b и c привязке и ядру, учитывая, что они имеют переменный размер?
  • Как передать член d, если он двумерный?
  • Как получить доступ к этим членам из ядра и какие типы будут объявлены как в аргументах ядра? Будет просто использовать нотацию индекса массива, т.е. B [0] работать для доступа?
  • Как бы я вызывал операцию, эквивалентную b.size() внутри функции ядра, или я бы не отказался и вместо этого передал размер от привязки к ядру в качестве дополнительного аргумента? Что произойдет, если он изменится?

Я бы очень признателен за исходный код кода C или С++ и код кода кода ядра в ответах.

Большое спасибо.

Ответ 1

  • Вы должны выделить буфер OpenCL и скопировать в него данные ЦП. Буфер 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), но это накладывает некоторые ограничения на выравнивание и доступ к памяти хоста после создания буфера. В основном, память хоста может содержать мусор, когда вы в настоящее время не отображаете его.

  • Это зависит. Величины ли размеры векторов во втором измерении равны? Затем просто сгладьте их при загрузке на устройство OpenCL. В противном случае это усложняется.

  • Вы объявляете аргументы буфера в качестве указателей __global в своем ядре. Например, __global double *b будет подходящим для буфера, созданного в 1. Вы можете просто использовать нотацию массива в ядре для доступа к отдельным элементам в буфере.

  • Вы не можете запрашивать размер буфера изнутри ядра, поэтому вам нужно передать его вручную. Это также может происходить неявно, например. если количество рабочих элементов соответствует размеру 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);