在OpenCL中有效地交换内存缓冲区:实现

时间:2015-06-02 11:12:42

标签: opencl

我遇到了与此处相同的问题:How to effectively swap OpenCL memory buffers?。我的第一个实现与问题中描述的相同,在每个周期它向/从设备写入/读取内存缓冲区。正如所指出的,这引入了无用的读/写缓冲器开销。下面的代码(带内存开销)工作正常:

//ARGUMENTS SWAP

f0_mem = ...
f1_mem = ...
m_d_mem = ...

//clEnqueueWriteBuffer occurs hear
writeBufferToDevice( (cl_mem&) f0_mem, (cl_mem&) f1_mem, (cl_mem&) m_d_mem, (int*) f0, (int*) f1, (int*) m_d);

for (int k = 0; k < numelem; k++) {

    /*
       The same code block
    */

    if (k % 2 == 0) {

        setKernelArgs(f0_mem, f1_mem, weight[k], value[k], (int) total_elements);

    } else {

        setKernelArgs(f1_mem, f0_mem, weight[k], value[k], (int) total_elements);

    }

    err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, global_work_items, NULL, 0, NULL, NULL);

    err = clEnqueueReadBuffer(queue, m_d_mem, CL_TRUE, 0, sizeof (int)*capacity, m_d, 0, NULL, NULL);                  

    memcpy(M + k*capacity, m_d, sizeof (int)*capacity);

}

编辑:我的内核:

//TWO KERNELS

f0_mem = ...
f1_mem = ...
m_d_mem = ...

//clEnqueueWriteBuffer occurs hear
writeBufferToDevice( (cl_mem&) f0_mem, (cl_mem&) f1_mem, (cl_mem&) m_d_mem, (int*) f0, (int*) f1, (int*) m_d);

for (int k = 0; k < numelem; k++) {

    /*
       The same code block
    */

    if (k % 2 == 0) {

        setKernelArgs(f0_mem, f1_mem, weight[k], value[k], (int) total_elements);
        clEnqueueNDRangeKernel(queue, kernel0, 1, NULL, global_work_items, NULL, 0, NULL, NULL);

    } else {

        setKernelArgs(kernel1, f1_mem, f0_mem, weight[k], value[k], (int) total_elements);
        clEnqueueNDRangeKernel(queue, kernel1, 1, NULL, global_work_items, NULL, 0, NULL, NULL);

    }

    clEnqueueReadBuffer(queue, m_d_mem, CL_TRUE, 0, sizeof (int)*capacity, m_d, 0, NULL, NULL);                  

    memcpy(M + k*capacity, m_d, sizeof (int)*capacity);

}

在我尝试实施两个建议的解决方案之后:

  1. 只需交换setKernelArgs(...)
  2. 创建两个内核
  3. 对于第一个我的代码:

    <script type="text/javascript" src="rectangles.json"></script>
    

    第二种解决方案以这种方式实施:

    <script>

    这两种解决方案都不适用于我(在我看来,根本没有发生交换!),我做错了什么?

    子问题:在最后两个解决方案中,是否可以在for循环之前使用零填充内存缓冲区而不使用writeBufferToDevice(f0_mem,f1_mem,m_d_mem ...)?

    这项工作基于这篇文章

2 个答案:

答案 0 :(得分:0)

两种尝试的解决方案对我来说都是正确的,但每次迭代之间可能存在一些依赖关系 - 您必须发布内核进行检查。 它在您的解决方案中工作正常可能是因为您正在编写和读取每个迭代,这些迭代工作得更慢,因此它有足够的时间来同步自身。 您可以尝试在每次OpenCL API调用后添加clFinish(command);,看看是否会产生影响。

除此之外,您还可以尝试第三种解决方案:在内核中交换指针。您需要将循环从CPU移动到GPU。

inline void swap_pointers(__global double **A, __global double **B)
{
    __global double *tmp = *A;
    *A = *B;
    *B = tmp;
}

__kernel void my_kernel(
__global double *pA,
__global double *pB,
...
)
{
    for (int k = 0; k < numelem; k++) 
    {

        // some stuff here

        swap_pointers(&pA, &pB);
        barrier(CLK_GLOBAL_MEM_FENCE | CLK_LOCAL_MEM_FENCE);
    }
}

然后在主机上一次性读取所有内容(m_d_mem必须足够大以存储来自所有迭代的数据):

clEnqueueReadBuffer(queue, m_d_mem, CL_TRUE, 0, sizeof (int)*capacity*numelem, m_d, 0, NULL, NULL);  

答案 1 :(得分:0)

<强>解决方案: 在将m_d复制到M之后的每个周期,应该重置m_d并使用Knapsack :: writeBuffer_m_d_ToDevice()

将其写回m_d_mem缓冲区对象
 ksack.readBuffer_m_d_FromDevice();            
 memcpy(M + k*capacity, m_d, sizeof (int)*capacity);
 ksack.writeBuffer_m_d_ToDevice();//resets m_d_mem