Cuda C

时间:2018-12-27 21:18:03

标签: c matrix cuda transpose

我想不出一种方法来使用CUDA C中的共享内存转置非平方矩阵。(我是CUDA C和C的新手)

在网站上:

  

https://devblogs.nvidia.com/efficient-matrix-transpose-cuda-cc/

演示了一种有效的方法来转置矩阵(通过共享内存进行Coalesced转置)。但这仅适用于平方矩阵。

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

在Stackoverflow上有一个类似的question。设置了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];
            }
        }
    }
}

其中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))

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

我插入了另一个if语句来确定将数据复制到哪里,因为有2(?)个可能的目的地,具体取决于ThreadIdx。目前true已插入其中,但我尝试了许多不同的操作。我能想到的最好的方法是if(threadIdx.x+1 < threadIdx.y+i),它成功地转换了3x2矩阵。

有人可以通过写回结果矩阵来说明我所缺少的吗?显然只有一个目的地是正确的。使用

  

matTran [n *(i_m + i)+ i_n] = tile [threadIdx.x] [threadIdx.y + i];

在博客上提到的应该是正确的,但我不知道为什么它不适用于非平方矩阵?

2 个答案:

答案 0 :(得分:1)

我使问题复杂化了。 Here,索引未按我的想法调换。使用线程/块的Y坐标和X坐标重新计算它们。这是代码段:

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

这是更正的代码:

__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!

        }
    }
}

感谢this评论,指出了该错误:)

答案 1 :(得分:-1)

如果您想进一步加快内核的速度,则可以使用“共享内存库冲突”,如下所示: https://developer.nvidia.com/blog/efficient-matrix-transpose-cuda-cc/

简而言之,使用此方法更改磁贴初始化将大有帮助:

__shared__ float tile[TILE_DIM][TILE_DIM+1];