ArrayFire: функция с ядром OpenCL, вызываемая из основной функции - PullRequest
0 голосов
/ 12 декабря 2018

функция следующая (извлекается из http://arrayfire.org/docs/interop_opencl.htm)

уникальная main функция

int main() {
    size_t length = 10;
    // Create ArrayFire array objects:
    af::array A = af::randu(length, f32);
    af::array B = af::constant(0, length, f32);
    // ... additional ArrayFire operations here
    // 2. Obtain the device, context, and queue used by ArrayFire
    static cl_context af_context = afcl::getContext();
    static cl_device_id af_device_id = afcl::getDeviceId();
    static cl_command_queue af_queue = afcl::getQueue();
    // 3. Obtain cl_mem references to af::array objects
    cl_mem * d_A = A.device<cl_mem>();
    cl_mem * d_B = B.device<cl_mem>();
    // 4. Load, build, and use your kernels.
    //    For the sake of readability, we have omitted error checking.
    int status = CL_SUCCESS;
    // A simple copy kernel, uses C++11 syntax for multi-line strings.
    const char * kernel_name = "copy_kernel";
    const char * source = R"(
        void __kernel
        copy_kernel(__global float * gA, __global float * gB)
        {
            int id = get_global_id(0);
            gB[id] = gA[id];
        }
    )";
    // Create the program, build the executable, and extract the entry point
    // for the kernel.
    cl_program program = clCreateProgramWithSource(af_context, 1, &source, NULL, &status);
    status = clBuildProgram(program, 1, &af_device_id, NULL, NULL, NULL);
    cl_kernel kernel = clCreateKernel(program, kernel_name, &status);
    // Set arguments and launch your kernels
    clSetKernelArg(kernel, 0, sizeof(cl_mem), d_A);
    clSetKernelArg(kernel, 1, sizeof(cl_mem), d_B);
    clEnqueueNDRangeKernel(af_queue, kernel, 1, NULL, &length, NULL, 0, NULL, NULL);
    // 5. Return control of af::array memory to ArrayFire
    A.unlock();
    B.unlock();
    // ... resume ArrayFire operations
    // Because the device pointers, d_x and d_y, were returned to ArrayFire's
    // control by the unlock function, there is no need to free them using
    // clReleaseMemObject()
    return 0;
}

, которые работают хорошо, так как конечные значенияиз B совпадают с таковыми из A, то есть af_print(B); соответствует A, но когда я пишу функции отдельно следующим образом:

отдельно main функция

arraycopy функция

void arraycopy(af::array A, af::array B,size_t length) {
    // 2. Obtain the device, context, and queue used by ArrayFire   
    static cl_context af_context = afcl::getContext();
    static cl_device_id af_device_id = afcl::getDeviceId();
    static cl_command_queue af_queue = afcl::getQueue();
    // 3. Obtain cl_mem references to af::array objects
    cl_mem * d_A = A.device<cl_mem>();
    cl_mem * d_B = B.device<cl_mem>();
    // 4. Load, build, and use your kernels.
    //    For the sake of readability, we have omitted error checking.
    int status = CL_SUCCESS;
    // A simple copy kernel, uses C++11 syntax for multi-line strings.
    const char * kernel_name = "copy_kernel";
    const char * source = R"(
        void __kernel
        copy_kernel(__global float * gA, __global float * gB)
        {
            int id = get_global_id(0);
            gB[id] = gA[id];
        }
    )";
    // Create the program, build the executable, and extract the entry point
    // for the kernel.
    cl_program program = clCreateProgramWithSource(af_context, 1, &source, NULL, &status);
    status = clBuildProgram(program, 1, &af_device_id, NULL, NULL, NULL);
    cl_kernel kernel = clCreateKernel(program, kernel_name, &status);
    // Set arguments and launch your kernels
    clSetKernelArg(kernel, 0, sizeof(cl_mem), d_A);
    clSetKernelArg(kernel, 1, sizeof(cl_mem), d_B);
    clEnqueueNDRangeKernel(af_queue, kernel, 1, NULL, &length, NULL, 0, NULL, NULL);
    // 5. Return control of af::array memory to ArrayFire
    A.unlock();
    B.unlock();
    // ... resume ArrayFire operations
    // Because the device pointers, d_x and d_y, were returned to ArrayFire's
    // control by the unlock function, there is no need to free them using
    // clReleaseMemObject()
}

main функция

int main()
{
    size_t length = 10;
    af::array A = af::randu(length, f32);
    af::array B = af::constant(0, length, f32);
    arraycopy(A, B, length);
    af_print(B);//does not match A
}

окончательные значения B не изменились, почему это происходит?и что я должен сделать, чтобы это работало ?, заранее спасибо

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

Причиной поведения, которое вы видите, является подсчет ссылок.Но это точно не ошибка, и она соответствует поведению языка C ++.

af :: array объекты при создании с использованием присваивания или эквивалентных операций выполняют только копирование метаданных и сохраняют общий доступуказатель.

В версии вашего кода, где это функция, B передается значением , поэтому внутренне B из arraycopy функция является копией метаданных B из функции main и разделение указателя на данные из массива B main.На этом этапе, если пользователь делает вызов device для извлечения указателя, мы предполагаем, что он предназначен для записи в местоположения этого указателя.Следовательно, когда устройство вызывается для массива, объект имеет общий указатель со счетчиком ссылок> 1, мы делаем копию исходного массива (B из основного) и возвращаем указатель на эту память.Поэтому, если вы сделаете af_print(B) внутри, вы увидите правильные значения.По сути, это копирование при записи - поскольку B передается по значению, вы не видите измененных результатов B из функции arraycopy.

В самой первой строке, которую я сказал, она соответствует поведению C ++потому что, если объект B нужно изменить из функции, он должен быть передан по ссылке.Передача его по значению только приводит к изменению значения внутри функции - именно так ArrayFire обрабатывает объекты af :: array.

Надеюсь, что это устранит путаницу.

Pradeep.Команда разработчиков ArrayFire.

0 голосов
/ 12 декабря 2018

Вы передаете af::array в arraycopy по значению, а не по ссылке, следовательно, A и B в main остаются неизменными независимо от того, что вы делаете внутри arraycopy.Вы можете передать B по ссылке: af::array &B в списке параметров.Я бы также рекомендовал передавать A по const-reference как обычай, чтобы избежать ненужных копий (const af::array &A).

...