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

在OpenCL中有效地交换内存缓冲区:实现,opencl,Opencl,我遇到了与这里相同的问题:。我的第一个实现与问题中描述的相同,它在每个周期向设备写入/读取内存缓冲区。如前所述,这会引入无用的读/写缓冲区开销。下面的代码(内存开销)运行良好: //THIS WORKS!!! f0_mem = clCreateBuffer( context, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, sizeof (int)*(capacity + 1), NULL

我遇到了与这里相同的问题:。我的第一个实现与问题中描述的相同,它在每个周期向设备写入/读取内存缓冲区。如前所述,这会引入无用的读/写缓冲区开销。下面的代码(内存开销)运行良好:

//THIS WORKS!!!

f0_mem = clCreateBuffer(
        context,
        CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, 
        sizeof (int)*(capacity + 1), 
        NULL, 
        &err);

f1_mem = (..."the same as above"...);
m_d_mem = clCreateBuffer(..., CL_MEM_WRITE_ONLY | CL_MEM_ALLOC_HOST_PTR, sizeof (int)*capacity,...);

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

    sumK = sumK - weight[k];
    cmax = 0;
    cmax = max(capacity - sumK, weight[k]);
    total_elements = (size_t) (capacity - cmax + 1);

    if (k % 2 == 0) {

        //clEnqueueWriteBuffer of cl_mem buffers
        writeBufferToDevice(f0_mem, f1_mem, f0, f1);
        setKernelArgs(f0_mem, f1_mem, weight[k], value[k], (int) total_elements);

    } else {

        //clEnqueueWriteBuffer of cl_mem buffers
        writeBufferToDevice(f1_mem, f0_mem, f1, f0);
        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);

    //clEnqueueReadBuffer of cl_mem buffers
    readBufferFromDevice(f0_mem, f1_mem, m_d_mem, f0, f1, m_d);

    memcpy(M + k*capacity, m_d, sizeof (int)*capacity);
}
//这很有效!!!
f0_mem=clCreateBuffer(
上下文
CL_MEM_READ_WRITE|CL_MEM_ALLOC_HOST|PTR,
sizeof(int)*(容量+1),
无效的
&错误);
f1_mem=(…“同上”…);
m_d_mem=clCreateBuffer(…,CL_mem_WRITE_ONLY | CL_mem_ALLOC_HOST_PTR,sizeof(int)*容量,…);
for(int k=0;k
编辑:我的内核:

void kernel knapsack(global int *input_f, global int *output_f, global int *m_d,  int cmax, int weightk, int pk, int maxelem){

int c = get_global_id(0)+cmax;

if(get_global_id(0) < maxelem){

    if(input_f[c] < input_f[c - weightk] + pk){
        output_f[c] = input_f[c - weightk] + pk;
        m_d[c-1] = 1;
    } 
    else{
    output_f[c] = input_f[c];

    }   
  }    
}
void内核背包(全局int*input\f,全局int*output\f,全局int*m\d,int-cmax,int-weightk,int-pk,int-maxelem){
INTC=获取全局id(0)+cmax;
if(获取全局id(0)
在我尝试实施两个建议的解决方案后:

  • 只需交换setKernelArgs(…)
  • 创建两个内核
  • 对于第一个,这是我的代码:

    //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);
    
    }
    
    //参数交换
    f0_mem=。。。
    f1_mem=。。。
    m_d_mem=。。。
    //clEnqueueWriteBuffer
    编写缓冲设备((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
    第二个解决方案是这样实现的:

    //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);
    
    }
    
    //两个内核
    f0_mem=。。。
    f1_mem=。。。
    m_d_mem=。。。
    //clEnqueueWriteBuffer
    编写缓冲设备((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
    这两种解决方案对我都不起作用(在我看来,根本不会发生交换!),我做错了什么

    子问题:在最后两种解决方案中,在for循环之前,是否可以在不使用writeBufferToDevice(f0_mem、f1_mem、m_d_mem…)的情况下使用零填充内存缓冲区

    这项工作基于这篇文章

    • 相关工作:

    两种尝试的解决方案在我看来都是正确的,但每次迭代之间可能存在一些依赖关系-您必须发布您的内核以进行检查。 它在您的解决方案中运行良好,可能是因为您正在编写和读取每个迭代,而这些迭代的运行速度较慢,因此有足够的时间来同步自身。 您可以尝试添加
    clFinish(命令)
    除此之外,您还可以尝试第三种解决方案:在内核中交换指针。您需要将循环从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复制到m之后的每个周期,m_d都应该重置,并使用背包::writeBuffer_m_d_ToDevice()写回m_d_mem buffer对象


    不幸的是,clFinish(命令队列)没有任何区别。您的解决方案很好,但对我的项目不可行。我必须使用大块缓冲区,因此它们不能放入设备内存,或者传输时间太长。@NicoMkhatvari您能发布最小可编译版本来重现问题吗?。值得注意的是:决策矩阵和所选项目打印输出在“交换缓冲区”的情况下是错误的,但在原始代码中是正确的。很明显,您在某个地方弄乱了缓冲区顺序。我看你的CL代码没有问题。顺便说一句,对于第二种情况,循环中不需要setKernelArgs。这就是为什么要有2个内核,否则只需使用第一个案例。感谢setKernelArgs注释,你是对的。你说的混用缓冲区是什么意思,它们在内核计算期间不会重叠,因为它们是分开的(在每次迭代中,一个缓冲区用作输入,另一个用作输出),而且我还有线程c
    clEnqueueReadBuffer(queue, m_d_mem, CL_TRUE, 0, sizeof (int)*capacity*numelem, m_d, 0, NULL, NULL);  
    
     ksack.readBuffer_m_d_FromDevice();            
     memcpy(M + k*capacity, m_d, sizeof (int)*capacity);
     ksack.writeBuffer_m_d_ToDevice();//resets m_d_mem