某些 CUDA 计算因块尺寸较大(< 1024)而失败

Posted

技术标签:

【中文标题】某些 CUDA 计算因块尺寸较大(< 1024)而失败【英文标题】:Some CUDA computations fail with larger block dimension (< 1024) 【发布时间】:2022-01-08 12:20:14 【问题描述】:

我正在使用 GTX 960 4GB 学习 CUDA。我编写了一个执行逐元素矩阵乘法的程序。当我将 x 和 y 的块尺寸增加到让我们说 (32 x 32) 结合一个大矩阵(让我们说 15000 x 15000 个元素)时,一些 但不是所有 乘法结果是错误的(值0 而不是 6)。

当我将块尺寸减小到例如 (8 x 8) 时,所有结果都再次正确。当我减小矩阵大小时,结果也再次正确。

所以在这个例子中,似乎有总线程数和每个块的线程数的组合,这是行不通的。

我很惊讶我找不到有关此主题的任何主题。我能找到的只是提高性能和占用率,而不是关于何时中止某些但不是所有计算。

网格尺寸计算如下:

dim3 blocks(ceil<int>(COLS / threads.x), ceil<int>(ROWS / threads.y));

为什么有些乘法失败而有些乘法成功?

一些例子

Block dim    : (8, 8)
Matrix shape : (15000, 15000)
Verification : 0 elements have failed, total length 225000000, shape: (15000, 15000)

Block dim    : (16, 16)
Matrix shape : (15000, 15000)
Verification : 239936 elements have failed, total length 225000000, shape: (15000, 15000)

Block dim    : (32, 32)
Matrix shape : (15000, 15000)
Verification : 719424 elements have failed, total length 225000000, shape: (15000, 15000).

Block dim    : (32, 32)
Matrix shape : (10000, 10000)
Verification : 0 elements have failed, total length 100000000, shape: (10000, 10000).

驱动版本

$ cat /proc/driver/nvidia/version
NVRM version: NVIDIA UNIX x86_64 Kernel Module  470.82.00  Thu Oct 14 10:24:40 UTC 2021

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <cuda.h>
#include <cuda_runtime.h>

#define ROWS 10000
#define COLS 10000
#define MAX_ERR 1e-6

typedef struct 
    int width;
    int height;
    float* elements;
 Matrix;

size_t ij(int i, int j)
    return j * ROWS + i;



__global__ void matrix_multi_elemwise(const Matrix OUT, const Matrix A, const Matrix B) 
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int row = blockIdx.y * blockDim.y + threadIdx.y;

    if (col < A.width && row < A.height) 
        int index = row * A.height + col;  // linearisation of index
        OUT.elements[index] = A.elements[index] * B.elements[index];
    


int main()
    Matrix A, B, OUT;
    Matrix dev_A, dev_B, dev_OUT; 

    size_t SIZE = ROWS * COLS * sizeof(float);

    // Allocate host memory
    A.elements = (float*) malloc(SIZE);
    B.elements = (float*) malloc(SIZE);
    OUT.elements = (float*) malloc(SIZE);

    // Initialize host matrices
    A.height = ROWS; A.width = COLS;
    B.height = ROWS; B.width = COLS;
    OUT.height = ROWS; OUT.width = COLS;

    for (int j = 0; j < ROWS; j++) 
        for(int i = 0; i < COLS; i++)
            A.elements[ij(i, j)] = 2.0f;
            B.elements[ij(i, j)] = 3.0f;
        
    

    // Allocate device memory
    cudaMalloc((void**) &dev_A.elements, SIZE);
    cudaMalloc((void**) &dev_B.elements, SIZE);
    cudaMalloc((void**) &dev_OUT.elements, SIZE);

    dev_A.height = A.height; dev_A.width = A.width;
    dev_B.height = A.height; dev_B.width = B.width;
    dev_OUT.height = A.height; dev_OUT.width = OUT.width;

    // Transfer data from host to device memory
    cudaMemcpy(dev_A.elements, A.elements, SIZE, cudaMemcpyHostToDevice);
    cudaMemcpy(dev_B.elements, B.elements, SIZE, cudaMemcpyHostToDevice);

    // Executing kernel 
    dim3 threads(16, 16);
    dim3 blocks(ceil<int>(COLS / threads.x), ceil<int>(ROWS / threads.y));
    matrix_multi_elemwise<<<blocks, threads>>>(dev_OUT, dev_A, dev_B);

    cudaError_t err = cudaGetLastError();
    if(err != cudaSuccess) 
        printf("CUDA Runtime API Error reported : %s in file %s on line.\n", cudaGetErrorString(err), __FILE__);
    
    
    // Wait for GPU to finish before accessing on host
    cudaDeviceSynchronize();  
    
    // Transfer data back to host memory
    cudaMemcpy(OUT.elements, dev_OUT.elements, SIZE, cudaMemcpyDeviceToHost);

    // Verification
    int count = 0, length = 0, i = 0, j = 0;
    for (j = 0; j < ROWS; j++) 
        for(i = 0; i < COLS; i++)
            //assert(fabs(OUT.elements[ij(i, j)] / A.elements[ij(i, j)] - B.elements[ij(i, j)]) < MAX_ERR);
            if (fabs(OUT.elements[ij(i, j)] / A.elements[ij(i, j)] - B.elements[ij(i, j)]) > MAX_ERR) 
                count++;
            
            length++;
        
    
    printf("Verification: %i elements have failed, total length %i, shape: (%i, %i).\n", count, length, i, j);

    // Deallocate device memory
    cudaFree(dev_A.elements);
    cudaFree(dev_B.elements);
    cudaFree(dev_OUT.elements);
    
    // Deallocate host memory
    free(A.elements); 
    free(B.elements); 
    free(OUT.elements);


【问题讨论】:

我怀疑你在某处遇到整数溢出。你的值正在逼近 32 位有符号整数运算的极限,我敢打赌你会在某个地方超越极限。 row * A.height + col 错误:它是row * A.width + col。这会导致非方阵出现问题。但这并不能解决您的主要问题。请注意,对列使用 i 是非常规的。矩阵的坐标A_(i,j) 表示第i 行和第j 列(如(m,n) 的大小)。 【参考方案1】:

块数错误。事实上,COLSthreads.x 都是整数。因此,结果是一个截断整数ceil&lt;int&gt; 无法限制结果,因为它已被截断。这会导致无法计算某些块:15000 可被 8 整除,但不能被 16 整除。您需要将 COLS 转换为浮点数或手动计算 ceil 结果(更安全)。这是一个例子:

dim3 blocks((COLS + threads.x - 1) / threads.x, (ROWS + threads.y - 1) / threads.y);

正如评论中所指出的,请注意row * A.height + col 是错误的:它应该是row * A.width + col。这会导致非方阵出现问题。

【讨论】:

谢谢!那解决了它。此外,我已经更正了展平功能并将矩阵索引调整为 ij 约定。

以上是关于某些 CUDA 计算因块尺寸较大(< 1024)而失败的主要内容,如果未能解决你的问题,请参考以下文章

使用 Cuda 并行实现计算大型数组中的大型连续子序列之和

如何以编程方式确定如何将较小的盒子装入较大的包装中? [关闭]

在较大屏幕尺寸下消失导航

cuda中如何选择block尺寸

(py)CUDA中的网格和块尺寸[重复]

C++ CUDA:为啥我的块尺寸不起作用?