Parallel processing 如何避免矩阵乘法CUDA内核中的非合并访问?

Parallel processing 如何避免矩阵乘法CUDA内核中的非合并访问?,parallel-processing,cuda,Parallel Processing,Cuda,我正在学习CUDA的书《大规模并行处理器编程》。第五章中的一个实践问题让我感到困惑: 对于超出的可能值范围的平铺矩阵乘法 BLOCK_SIZE,对于BLOCK_SIZE的值,内核将完全 避免对全局内存的未合并访问?(你只需要考虑方块) 据我所知,块大小对内存聚合几乎没有影响。只要单个warp中的线程访问连续的元素,我们就可以进行合并访问。我不知道内核在哪里对全局内存进行了未合并的访问。你们有什么提示吗 以下是内核的源代码: #define COMMON_WIDTH 512 #define ROW

我正在学习CUDA的书《大规模并行处理器编程》。第五章中的一个实践问题让我感到困惑:

对于超出的可能值范围的平铺矩阵乘法 BLOCK_SIZE,对于BLOCK_SIZE的值,内核将完全 避免对全局内存的未合并访问?(你只需要考虑方块)

据我所知,块大小对内存聚合几乎没有影响。只要单个warp中的线程访问连续的元素,我们就可以进行合并访问。我不知道内核在哪里对全局内存进行了未合并的访问。你们有什么提示吗

以下是内核的源代码:

#define COMMON_WIDTH 512
#define ROW_LEFT 500 
#define COL_RIGHT 250
#define K 1000
#define TILE_WIDTH 32
__device__ int D_ROW_LEFT = ROW_LEFT;
__device__ int D_COL_RIGHT = COL_RIGHT;
__device__ int D_K = K;
.....
__global__
void MatrixMatrixMultTiled(float *matrixLeft, float *matrixRight, float *output){
    __shared__  float sMatrixLeft[TILE_WIDTH][TILE_WIDTH];
    __shared__  float sMatrixRight[TILE_WIDTH][TILE_WIDTH];  
   int bx = blockIdx.x; int by = blockIdx.y;
   int tx = threadIdx.x; int ty = threadIdx.y;
   int col = bx * TILE_WIDTH + tx;
   int row = by * TILE_WIDTH + ty;
   float value = 0;
   for (int i = 0; i < ceil(D_K/(float)TILE_WIDTH); ++i){
       if (row < D_ROW_LEFT && row * D_K + i * TILE_WIDTH  +tx < D_K){
        sMatrixLeft[ty][tx]  = matrixLeft[row * D_K + i * TILE_WIDTH  +tx];
       }
       if (col < D_COL_RIGHT && (ty + i * TILE_WIDTH) * D_COL_RIGHT  + col < D_K ){
        sMatrixRight[ty][tx] = matrixRight[(ty + i * TILE_WIDTH) * D_COL_RIGHT  + col];
       }
       __syncthreads();
       for (int j = 0; j < TILE_WIDTH; j++){
           value += sMatrixLeft[ty][j] * sMatrixRight[j][tx]; 
       }
       __syncthreads();
   }
   if (row < D_ROW_LEFT && col < D_COL_RIGHT ){
        output[row * D_COL_RIGHT + col] = value;
       }
}
#定义公共宽度512
#定义左500行
#定义COL_RIGHT 250
#定义K1000
#定义平铺宽度32
__device_uuuuint D_ROW_LEFT=ROW_LEFT;
__设备内部数据列右=列右;
__设备uuu int D_K=K;
.....
__全球的__
无效矩阵矩阵矩阵结果平铺(浮点*矩阵左、浮点*矩阵右、浮点*输出){
__共享浮点数sMatrixLeft[平铺宽度][平铺宽度];
__共享浮动sMatrixRight[平铺宽度][平铺宽度];
int bx=blockIdx.x;int by=blockIdx.y;
int tx=threadIdx.x;int ty=threadIdx.y;
int col=bx*瓷砖宽度+tx;
int row=按*平铺宽度+ty;
浮点数=0;
对于(int i=0;i
您的问题是不完整的,因为您发布的代码没有提及
块大小,这肯定至少与书中提出的问题非常相关。更一般地说,在没有启动配置的情况下对内核提出的问题通常是不完整的,因为启动配置通常与内核的正确性和行为相关

我现在还没有重读这本书的这一部分。然而,我将假设内核启动配置包含一个如下所示的块维度:(您的问题中没有此信息,但在我看来,对于一个合理的问题,应该包含此信息)

我将假设内核启动是由以下内容给出的:

MatrixMatrixMultTiled<<<dimGrid, dimBlock>>>(...);
现在,我们来举一个例子,假设
BLOCK\u SIZE
为16。上述任何访问点是否会违反您的语句“单扭曲内的线程访问连续元素”

让我们从块(0,0)开始。因此,
row
等于
threadIdx.y
col
等于
threadIdx.x
。让我们考虑那个块的第一个翘曲。因此,该扭曲中的前16条线程的
threadIdx.y
值为0,其
threadIdx.x
值将从0..15增加。同样,该扭曲中的后16条线程的
threadIdx.y
值为1,其
threadIdx.x
值将从0..15增加

现在,让我们计算为上面的第一个访问点生成的实际索引,跨越扭曲。假设我们在第一次循环迭代中,
i
为零。因此:

matrixLeft[row * D_K + i * TILE_WIDTH  +tx];
减少到:

matrixLeft[threadIdx.y * D_K + threadIdx.x];
duk
这里只是
K
变量的设备副本,即1000。现在,让我们在选定块(0,0)中的选定扭曲(0)上计算上面的缩减索引表达式:

因此,此处生成的索引模式显示扭曲中第16和第17条线程之间的不连续性,并且访问模式不符合您之前所述的条件:

“单扭曲内的线程访问连续元素”


在这种情况下,我们没有合并访问权限(至少对于
float
数量而言)。

我猜他们指的是瓷砖宽度,而不是块大小,这是一本非常古老的书,我不推荐它作为第一个指南。自从那本书问世近10年以来,硬件和软件都有了长足的发展ago@talonmies谢谢你的回复。我想罗伯特·克罗维拉澄清了我的想法。现在,我已经在2016年出版了《大规模并行处理器编程》一书的第三版。太旧了不能用吗?非常感谢你,罗伯特!你的解释解决了我的困惑。我想这个问题是想问我如何配置matrix_乘法内核,以避免在访问全局内存时进行非合并访问。为此,我们需要确保dimBlock.x可以除以32(扭曲的大小)。我说得对吗?是的,32的x块尺寸应该可以合并访问
matrixLeft[row * D_K + i * TILE_WIDTH  +tx];
matrixLeft[threadIdx.y * D_K + threadIdx.x];
warp lane:    0  1  2  3  4  5  6  .. 15     16   17   18 .. 31
threadIdx.x   0  1  2  3  4  5  6     15      0    1    2    15
threadIdx.y   0  0  0  0  0  0  0      0      1    1    1     1
index:        0  1  2  3  4  5  6     15   1000 1001 1002  1015