Memory 在OpenCL中写入全局内存

Memory 在OpenCL中写入全局内存,memory,opencl,Memory,Opencl,我试图优化一些最初用Fortran编写的代码 该算法涉及在多次迭代中对大型阵列(约2700万个单元)进行操作。每个单元可以在一次迭代中独立地进行评估。但是,由于在t+1处进行的计算取决于在t处进行的计算结果,因此无法并行化迭代 一个粗略简化的非并行伪代码示例: for (t=0; t<tmax; t++) { A = A + B; B = B + A /2; } 为了澄清算法,我简化了内核代码。但是我的想法是基于B更新A;然后我根据A更新B。这会重复几次。没有办法完全避免全局写入问题

我试图优化一些最初用Fortran编写的代码

该算法涉及在多次迭代中对大型阵列(约2700万个单元)进行操作。每个单元可以在一次迭代中独立地进行评估。但是,由于在t+1处进行的计算取决于在t处进行的计算结果,因此无法并行化迭代

一个粗略简化的非并行伪代码示例:

for (t=0; t<tmax; t++)
{
A = A + B;

B  = B + A /2;
}

为了澄清算法,我简化了内核代码。但是我的想法是基于B更新A;然后我根据A更新B。这会重复几次。

没有办法完全避免全局写入问题。您只需编写一次值,并且速度受硬件限制。不过,只要不介意同时计算多个步骤,就可以减少全局读取的数量。这仍然可以节省整个过程中的每一步

__kernel void myKernel(__global double *A, __global double *B, __global uint outDataMultiple)              
{                                                                                      
    const uint gid = get_global_id(0);
    const uint inDataSize = get_global_size(0);

    double2 nextValue;
    nextValue.x = A[gid];
    nextValue.y = B[gid];
    for(uint i=0; i<outDataMultiple; i++){
        nextValue.x = nextValue.x + nextValue.y;
        nextValue.y = nextValue.y + nextValue.x /2;
        A[gid+i+1] = nextValue.x;
        B[gid+i+1] = nextValue.y;
    }
}
\uuuuu kernel void myKernel(\uuuu global double*A、\uuuu global double*B、\uuuu global uint outDataMultiple)
{                                                                                      
const uint gid=获取全局id(0);
const uint inDataSize=获取全局大小(0);
双2下一个值;
nextValue.x=A[gid];
nextValue.y=B[gid];

对于(uint i=0;i来说,减少OpenCL设备从全局内存中获取数据所需时间的一个简单方法是将全局内存缓冲到本地内存,对本地内存进行操作,然后将本地内存批量写入全局内存

本地内存与线程内存的延迟基本相同,可以从全局内存中分块读取。本地内存可以在主机上声明并传递给内核(见下面的示例),也可以在内核中分配并使用(见下面列出的AMD优化指南中的示例)。例如:

__kernel void kernA(__global double *A, 
                    __global double *B, 
                    __local double *BufferA,
                    __local double *BufferB)
    {
    BufferA[get_local_id(0)] = A[get_global_id(0)];
    BufferB[get_local_id(0)] = B[get_global_id(0)];
    mem_fence(CLK_LOCAL_MEM_FENCE);

    double tmp = BufferA[get_local_id(0)] + BufferB[get_local_id(0)];

    A[get_global_id(0)] = BufferA[get_local_id(0)];
    mem_fence(CLK_GLOBAL_MEM_FENCE);
    }
还可以做其他事情,包括:

  • 内存平铺-使用2d全局ID访问内存
  • 优化设备的本地内存大小
  • 如果可以,请使用浮点
  • 尽可能多地填充到内核中

为了计算新A或B的
(i,j)
条目,您是否需要引用
(i,j)以外的任何内容
旧A和B的条目?如果不是,那么除了第一次迭代的输入和最后一次迭代的输出之外,您不需要使用全局内存,您可以在单个内核调用中计算许多迭代。我只需要知道旧A(I)和B(I)来自上一次迭代。您是否建议在内核中运行迭代循环?这会保留迭代顺序吗?单元格由什么组成?单个值?int、float、double或其他值?您可能可以在同一个内核调用中计算多个迭代。您能发布内核和C/C++代码吗你以任何方式使用本地内存吗?向量?A,B是双精度的,包含约27M个单元格。将在一瞬间发布代码的相关部分。严格从概念上讲,步骤1如何。从全局内存加载内容2。开始循环3。运行A=f(B),B=f(A)在private 4.end loop 5.copy back to global?我认为这些都失败了,因为寄存器中没有足够的可用内存,而且因为opencl生成了一堆线程,循环将独立地在每个单元格上运行(这是不好的,因为a[I]依赖于例如b[I],但b[I+1])。27M个线程不会同时启动。驱动程序/硬件将为您管理所有这些。您仍应使用EnqueuenRange指定工作组大小。我看不出在计算[I+1]时需要B[I+1]的位置在您的示例代码中。我列出的代码将复制原始循环,但在不同的内存位置,因此您可以对每次outDataMultiple Write进行一次全局读取。
__kernel void myKernel(__global double *A, __global double *B, __global uint outDataMultiple)              
{                                                                                      
    const uint gid = get_global_id(0);
    const uint inDataSize = get_global_size(0);

    double2 nextValue;
    nextValue.x = A[gid];
    nextValue.y = B[gid];
    for(uint i=0; i<outDataMultiple; i++){
        nextValue.x = nextValue.x + nextValue.y;
        nextValue.y = nextValue.y + nextValue.x /2;
        A[gid+i+1] = nextValue.x;
        B[gid+i+1] = nextValue.y;
    }
}
__kernel void myKernel(__global double2 *data, __global uint outDataMultiple)              
{                                                                                      
    const uint gid = get_global_id(0);
    const uint inDataSize = get_global_size(0);

    double2 nextValue = data[gid];
    for(uint i=0; i<outDataMultiple; i++){
        nextValue.x = nextValue.x + nextValue.y;
        nextValue.y = nextValue.y + nextValue.x /2;
        data[gid+i+1] = nextValue;
    }
}
__kernel void kernA(__global double *A, 
                    __global double *B, 
                    __local double *BufferA,
                    __local double *BufferB)
    {
    BufferA[get_local_id(0)] = A[get_global_id(0)];
    BufferB[get_local_id(0)] = B[get_global_id(0)];
    mem_fence(CLK_LOCAL_MEM_FENCE);

    double tmp = BufferA[get_local_id(0)] + BufferB[get_local_id(0)];

    A[get_global_id(0)] = BufferA[get_local_id(0)];
    mem_fence(CLK_GLOBAL_MEM_FENCE);
    }