Cuda 如何确保编译器并行化全局内存中的加载?

Cuda 如何确保编译器并行化全局内存中的加载?,cuda,gpu,Cuda,Gpu,我编写了一个CUDA内核,看起来像这样: int tIdx = threadIdx.x; // Assume a 1-D thread block and a 1-D grid int buffNo = 0; for (int offset=buffSz*blockIdx.x; offset<totalCount; offset+=buffSz*gridDim.x) { // Select which "page" we're using on this iteration

我编写了一个CUDA内核,看起来像这样:

int tIdx = threadIdx.x; // Assume a 1-D thread block and a 1-D grid
int buffNo = 0;
for (int offset=buffSz*blockIdx.x; offset<totalCount; offset+=buffSz*gridDim.x) {
    // Select which "page" we're using on this iteration
    float *buff = &sharedMem[buffNo*buffSz];
    // Load data from global memory
    if (tIdx < nLoadThreads) {
        for (int ii=tIdx; ii<buffSz; ii+=nLoadThreads)
            buff[ii] = globalMem[ii+offset];
    }
    // Wait for shared memory
    __syncthreads();
    // Perform computation
    if (tIdx >= nLoadThreads) {
        // Perform some computation on the contents of buff[]
    }
    // Switch pages
    buffNo ^= 0x01;
}
int tIdx=threadIdx.x;//假设一个一维线程块和一个一维网格
int-buffNo=0;

对于(int offset=buffSz*blockIdx.x;offset,编译器没有机会将存储重新排序到共享内存,而不从全局内存加载,因为紧跟其后的是一个
\u syncthreads()
障碍。 由于所有离线线程都必须在屏障处等待,因此使用更多线程进行加载速度更快。这意味着可以随时执行更多的全局内存事务,并且每个加载线程必须减少全局内存延迟

到目前为止,所有CUDA设备都不支持无序执行,因此每次循环迭代加载循环将产生一次全局内存延迟,除非编译器可以在存储之前展开它并重新排序加载

为了允许完全展开,需要在编译时知道循环迭代的次数。您可以使用Talonmes的建议,将循环行程模板化以实现这一点

您还可以使用部分展开。使用
#pragma unroll 2
注释加载循环将允许编译器发出两次加载,然后每两次循环迭代两次存储,从而实现与加倍
nLoadThreads
类似的效果。可以用更高的数字替换
2
,但会达到最大数值在某一点上正在运行的事务的ber(使用float2或float4移动以传输具有相同事务数的更多数据)。此外,很难预测编译器是否会倾向于对指令进行重新排序,而不是为最终(可能是部分)展开的循环执行更复杂的代码

因此,建议是:

  • 使用尽可能多的加载线程
  • 通过模板化循环迭代次数并实例化所有可能的循环次数(或最常见的循环次数,具有通用回退),或通过使用部分循环展开,展开负载循环
  • 如果数据已适当对齐,则将其移动为
    float2
    float4
    ,以移动具有相同事务数的更多数据

  • 这是一个gendankenexperiment,还是您试图用实际代码解决实际问题?从您的问题来看,您似乎关心的编译器行为是否真的发生了,或者这是否只是一个大声思考的练习,这并不明显。对不起,我可以看出这有多混乱,我已经将问题编辑为clar这是一个实际的问题,这是一个简化的代码片段,这是一个真正的编译器行为。我很想知道这个问题的答案,即使答案是“不,您描述的优化对于编译器工具链来说太难了”。我认为如果没有真正的代码进行分析,就很难给出答案。我的第一反应是尝试对循环行程进行模板化。如果只有有限数量的不同缓冲区大小,那么实例化它们就不会太难了。然后编译器可能会展开循环。但这非常困难换句话说,没有更具体的东西可以看。你为什么要使用比可用线程更少的线程进行加载?@tera:在某个点上,增加每个块的加载线程数限制了每个MP的块数。对于一些问题大小,这会立即发生。这让我产生疑问,“当单个线程(具有展开的加载循环)可以同时运行多个LD时,为什么要使用更多线程进行加载?”感谢您的建议!(0)我尝试了
    #pragma unroll
    ,发现编译器没有选择对指令重新排序。(1)在某些情况下,“尽可能多地”“=1扭曲;添加更多扭曲会减少块/MP的数量。(2)模板化行程计数是一个有趣的技巧!我将看看这是否会产生重新排序的指令。(3)
    float4
    是一个好主意!我不知道这些存在(我是CUDA的新手)我想这会有很大帮助。我会尝试一下,让你知道它是如何运行的。
    float4
    和模板化循环计数(以便在编译时知道加载循环行程计数)的组合是一个赢家。编译器没有对所有的LD/ST重新排序,但是有四个128位的加载(每个线程)足够让计算隐藏内存延迟。