c - CUDA 中更快的矩阵乘法

标签 c cuda matrix-multiplication

目前,我在 cuda c 中做了一个神经网络程序。因为我需要操作矩阵乘法,所以我没有将 CUBLAS 用于 MM。我对MM使用以下代码。我想知道是否有人有一些建议可以让它更快,这会很有帮助,因为我需要在学习过程中使用 MM 数百万次。谢谢。 这是 MakeFile:

# cuda root
_CUDA_ROOT_ = /usr/local/cuda

NVCC = nvcc
# include and lib paths
INCLUDES=-I${_CUDA_ROOT_}/include
LIB_PATH=-L${_CUDA_ROOT_}/lib64

# libraries to link against
LIB= -lcudart -lcublas
CU_SRC= main.cu
EXE=$(CU_SRC:.cu=)
#------------------------------
# Choose your gpu arch
SM = sm_35
all: $(EXE)
$(EXE): $(CU_SRC)
        $(NVCC) -arch $(SM) $(CU_SRC) -o $(EXE) $(LIB_PATH) $(LIB)

clean:
        rm -f *.o *.cu_o $(EXE)

这是MM代码:

__global__
void matrixMulti(float* A_d, float* B_d, float* C_d, int m, int k, int n)
{
    __shared__ float ds_A[TILE_WIDTH][TILE_WIDTH];
    __shared__ float ds_B[TILE_WIDTH][TILE_WIDTH];
    int col = blockIdx.x*blockDim.x + threadIdx.x;
    int row = blockIdx.y*blockDim.y + threadIdx.y;
    int tx = threadIdx.x;
    int ty = threadIdx.y;
    float sum = 0;

    for(int t=0; t<(n-1)/TILE_WIDTH+1; t++)
    {
        if(row<m && t*TILE_WIDTH+tx<n)
            ds_A[ty][tx] = A_d[row*n + t*TILE_WIDTH+tx];
        else
            ds_A[ty][tx] = 0.0;
        if(t*TILE_WIDTH+ty<n && col<k)
            ds_B[ty][tx] = B_d[(t*TILE_WIDTH+ty)*k + col];
        else
            ds_B[ty][tx] = 0.0;
        __syncthreads();
        for(int i=0; i<TILE_WIDTH; i++)
            sum += ds_A[ty][i] * ds_B[i][tx];
        __syncthreads();
    }
    if(row<m && col<k)
        C_d[col+row*k] = sum;
}

这是代码主要部分的示例:

const int TILE_WIDTH = 32;

int main()
{
    int m, k, n;
    m = 10000, k = 10000, n = 10000;
    float *A, *B, *C;
    A = new float[m*n];
    B = new float[n*k];
    C = new float[m*k];
    float *A_d, *B_d, *C_d;
    for (int i=0; i<m*n; i++)
    {
        A[i] = 2;
    }
    for (int i=0; i<n*k; i++)
    {
        B[i] = 3;
    }
    cudaMalloc(&A_d, sizeof(float)*m*n);
    cudaMalloc(&B_d, sizeof(float)*n*k);
    cudaMalloc(&C_d, sizeof(float)*m*k);
    cudaMemcpy(A_d, A, sizeof(float)*m*n, cudaMemcpyHostToDevice);
    cudaMemcpy(B_d, B, sizeof(float)*k*n, cudaMemcpyHostToDevice);
    dim3 dimGrid((k-1)/TILE_WIDTH+1, (m-1)/TILE_WIDTH+1, 1);
    dim3 dimBlock(TILE_WIDTH, TILE_WIDTH, 1);
    matrixMulti<<<dimGrid,dimBlock>>>(A_d, B_d, C_d, m, k, n);
    cudaMemcpy(C, C_d, sizeof(float)*m*k, cudaMemcpyDeviceToHost);
    return 0;
}

最佳答案

首先,要确定这是你想要做的。如果不描述您想要执行的操作,很难对此发表评论,但请注意矩阵乘法是一个 n 立方运算。如果您的操作不是相同的复杂性,那么您很可能仅使用 cuBLAS 会做得更好。

这是为什么? cuBLAS 可能会比您编写的任何东西都快,并且更易于维护,因为它将遵循新的 GPU 架构。 GEMM 之类的最佳实现会因架构而异,因此您现在为硬件编写的任何代码都必须针对新硬件进行重新优化。

现在,问题来了。您应该考虑多种技术来优化此代码:

  1. 计算每个线程的多个输出值。这可以减轻共享内存的压力,因为切片数据可用于多次计算。
  2. 修复共享内存中的银行冲突。文档应该很好地涵盖了这一点。
  3. 矢量化共享内存加载和存储。我注意到您正在为 sm_35 进行编译。该架构的共享内存库每个都具有 64 位/时钟的带宽。加载单个 float 只有 32 位,因此如果没有矢量化,您将无法获得 float 的全部带宽。你应该看看 float2/float4 类型。
  4. 考虑双缓冲。将数据加载到一个共享内存块中,同时对另一个 block 进行操作。这允许更有效地隐藏全局内存操作的高延迟,减少同步开销,并且通常倾向于执行更好。不过,它使用的共享内存是您一次需要两个磁贴的两倍。

有很多关于在 GPU 上实现矩阵乘法的论文,我建议你去看看。您将从这些论文中获得比提出关于 SO 的广泛问题更多的详细信息。

最后...您确定不想使用 cuBLAS 吗?我不会指望获得 75% 的 cuBLAS 性能,即使这样也将是一个挑战。

关于c - CUDA 中更快的矩阵乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30703190/

相关文章:

parallel-processing - CUDA/OpenCL 中的深度优先搜索

javascript - 在 Javascript 中乘以 2 个矩阵

c++ - 使用 thrust::for_each() 调用实现 CUDA 流时访问结构的结果

memory - CUDA 常量内存分配是如何工作的?

C - 使用 ncurses 获取中性背景

c - 即使在向数组中添加更多具有值的元素后,数组的大小仍保持不变

c++ - 使用 shmat 和 shmget 与 forks 相乘矩阵

arrays - 如何在 Matlab 中更有效地进行以下矩阵乘法?

c - 如何使用 getchar() 扫描一个单词

c - (C) - 字符串数组中的第一个元素正在变成垃圾