Cuda C 上具有任意大小的矩阵转置(具有共享内存)

Lux*_*xii 2 c transpose cuda matrix gpu-shared-memory

我无法找到在 CUDA C 中使用共享内存转置非平方矩阵的方法。(我是 CUDA C 和 C 的新手)

这篇博客文章中,展示了如何转置矩阵的有效方法(通过共享内存合并转置)。但它只适用于方阵。

github上也提供了代码(与博客上相同)。

StackOverflow 上也有类似的问题。有TILE_DIM = 16设定。但通过该实现,每个线程只需将矩阵的一个元素复制到结果矩阵。

这是我当前的实现:

__global__ void transpose(double* matIn, double* matTran, int n, int m){
    __shared__ double tile[TILE_DIM][TILE_DIM];
    int i_n = blockIdx.x*TILE_DIM + threadIdx.x;
    int i_m = blockIdx.y*TILE_DIM + threadIdx.y; // <- threadIdx.y only between 0 and 7

    // Load matrix into tile
    // Every Thread loads in this case 4 elements into tile.
    int i;
    for (i = 0; i < TILE_DIM; i += BLOCK_ROWS){
        if(i_n < n  && (i_m+i) < m){
            tile[threadIdx.y+i][threadIdx.x] = matIn[n*(i_m+i) + i_n];
        } else {
            tile[threadIdx.y+i][threadIdx.x] = -1; 
        }
    }
    __syncthreads();

    for (i = 0; i < TILE_DIM; i += BLOCK_ROWS){
        if(tile[threadIdx.x][threadIdx.y+i] != -1){ // <- is there a better way?
            if(true){      // <- what should be checked here?
                matTran[n*(i_m+i) + i_n] = tile[threadIdx.x][threadIdx.y+i];
            } else {
                matTran[m*i_n + (i_m+i)] = tile[threadIdx.x][threadIdx.y+i];
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其中 4 个元素从线程复制到图块中。此外,图块中的四个元素也被复制回结果矩阵中。

这里是内核配置<<<a, b>>>

where a: (ceil(n/TILE_DIM), ceil(n/TILE_DIM))  (-> is casted to doubles) and 
      b: (TILE_DIM, BLOCK_ROWS) (-> (32, 8))
Run Code Online (Sandbox Code Playgroud)

我目前正在使用if(tile[threadIdx.x][threadIdx.y+i] != -1)- 语句来确定哪个线程应该复制到结果矩阵(可能还有另一种方法)。就我目前的知识而言,其行为如下:在块中,线程索引(x, y)将数据复制到图块中,线程索引(y, x)将数据复制回结果矩阵中。

我插入了另一个if- 语句来确定复制数据的位置,因为有 2(?) 个可能的目的地,具体取决于线程索引。目前true已插入那里,但我尝试了很多不同的东西。我能想到的最好的办法是if(threadIdx.x+1 < threadIdx.y+i),它3x2成功地转置了 -matrix 。

有人可以解释一下,我通过写回到结果矩阵中缺少什么吗?显然只有一个目的地是正确的。使用

matTran[n*(i_m+i) + i_n] = tile[threadIdx.x][threadIdx.y+i];
Run Code Online (Sandbox Code Playgroud)

正如博客中提到的应该是正确的,但我不明白,为什么它不适用于非平方矩阵?

Lux*_*xii 5

我把问题过于复杂化了。在这里,索引并没有像我想象的那样交换。它们是使用线程/块的 Y 坐标和 X 坐标重新计算的。这是片段:

i_n = blockIdx.y * TILE_DIM + threadIdx.x;  
i_m = blockIdx.x * TILE_DIM + threadIdx.y
Run Code Online (Sandbox Code Playgroud)

这是更正后的代码:

__global__ void transposeGPUcoalescing(double* matIn, int n, int m, double* matTran){
    __shared__ double tile[TILE_DIM][TILE_DIM];
    int i_n = blockIdx.x * TILE_DIM + threadIdx.x;
    int i_m = blockIdx.y * TILE_DIM + threadIdx.y; // <- threadIdx.y only between 0 and 7

    // Load matrix into tile
    // Every Thread loads in this case 4 elements into tile.
    int i;
    for (i = 0; i < TILE_DIM; i += BLOCK_ROWS){
        if(i_n < n  && (i_m+i) < m){
            tile[threadIdx.y+i][threadIdx.x] = matIn[(i_m+i)*n + i_n];
        }
    }
    __syncthreads();

    i_n = blockIdx.y * TILE_DIM + threadIdx.x; 
    i_m = blockIdx.x * TILE_DIM + threadIdx.y;

    for (i = 0; i < TILE_DIM; i += BLOCK_ROWS){
        if(i_n < m  && (i_m+i) < n){
            matTran[(i_m+i)*m + i_n] = tile[threadIdx.x][threadIdx.y + i]; // <- multiply by m, non-squared!

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢评论注意到错误:)