CUDA 无法使用所有可用的常量内存

edh*_*dhu 2 cuda gpu nvidia

我有一个使用协作组来执行某些操作的代码。因此我用以下方法编译我的代码:

/usr/local/cuda/bin/nvcc -arch=sm_61 -gencode=arch=compute_61,code=sm_61, --device-c -g -O2 foo.cu
Run Code Online (Sandbox Code Playgroud)

然后我尝试调用设备链接器:

/usr/local/cuda/bin/nvcc -arch=sm_61 -gencode=arch=compute_61,code=sm_61, -g -dlink foo.o
Run Code Online (Sandbox Code Playgroud)

然后它会产生错误:

ptxas 错误:文件使用太多全局常量数据(0x10100 字节,最大 0x10000)

该问题是由我分配常量内存的方式引起的:

__constant__ float d_cnst_centers[CONST_MEM / sizeof(float)];
Run Code Online (Sandbox Code Playgroud)

其中 CONST_MEM = 65536 字节,这是我从 SM_61 的设备查询中获得的。但是,如果我将常量内存减少到 64536 之类的值,问题就消失了。这几乎就像在编译期间为了某些目的而“保留”常量内存一样。我搜索了 CUDA 文档,但没有找到满意的答案。使用可用的最大常量内存是否安全?为什么会出现这个问题呢?

编辑:这是在 SM_61 上触发错误的代码片段:

#include <algorithm>
#include <vector>
#include <type_traits>
#include <cuda_runtime.h>
#include <cfloat>
#include <iostream>

#include <cooperative_groups.h>

using namespace cooperative_groups;


struct foo_params {
    float * points;
    float * centers;
    int * centersDist;
    int * centersIndex;
    int numPoints;
};

__constant__ float d_cnst_centers[65536 / sizeof(float)];

template <int R, int C>
__device__ int 
nearestCenter(float * points, float * pC) {
    float mindist = FLT_MAX;
    int minidx = 0;
    int clistidx = 0;
    for(int i=0; i<C;i++) {
        clistidx = i*R;
        float dist;
        {
            float *point = points;
            float *center = &pC[clistidx];
            float accum;
            for(int i = 0; i<R; i++) {
                float delta = point[i] - center[i];
                accum += delta*delta;
            }
            dist = sqrt(accum);
        }
        /* ... */
    }
    return minidx;
}


template<int R, int C, bool bRO, bool ROWMAJ=true>
__global__ void getNeatestCenter(struct foo_params params) {
        float * points = params.points;
        float * centers = params.centers;
        int * centersDist = params.centersDist;
        int * centersIndex = params.centersIndex;
        int numPoints = params.numPoints;

        grid_group grid = this_grid();
        {
            int idx = blockIdx.x*blockDim.x+threadIdx.x;
            if (idx < numPoints) {
                centersIndex[idx] = nearestCenter<R,C>(&points[idx*R], d_cnst_centers);
            }
        }
        /* ... other code */
}

int main () {
    // foo paramaters, for illustration purposes
    struct foo_params param;
    param.points = NULL;
    param.centers = NULL;
    param.centersDist = NULL;
    param.centersIndex = NULL;
    param.numPoints = 1000000;
    void *p_params = &param;

    int minGridSize = 0, blockSize = 0;
    cudaOccupancyMaxPotentialBlockSize(
                            &minGridSize,
                            &blockSize,
                            (void*)getNeatestCenter<128, 64, true>,
                            0,
                            0);

    dim3 dimGrid(minGridSize, 1, 1), dimBlock(blockSize, 1, 1);

    cudaLaunchCooperativeKernel((void *)getNeatestCenter<32, 32, true>, dimGrid, dimBlock, &p_params);
}
Run Code Online (Sandbox Code Playgroud)

该问题似乎是由以下行引起的:

grid_group grid = this_grid();
Run Code Online (Sandbox Code Playgroud)

它似乎在没有已知原因的情况下使用了大约 0x100 字节的常量内存。

nju*_*ffa 5

这个答案是推测性的,因为OP没有提供最小但完整的重现代码。

GPU 包含多个常量内存库,用于程序存储的不同部分。这些库之一供程序员使用。重要的是,CUDA 标准数学库代码使用相同的库,因为数学库代码通过函数内联成为程序员代码的一部分。在过去,这是显而易见的,因为整个 CUDA 数学库最初只是几个头文件。

某些数学函数内部需要小型常量数据表。具体的例子是sin,,,costan当使用这些数学函数时,__constant__程序员可用的数据量会从 64KB 少量减少。以下是一些用于演示目的的示例程序,使用 CUDA 8 工具链编译-arch=sm_61

#include <stdio.h>
#include <stdlib.h>

#define CONST_MEM (65536)
__constant__ float d_cnst_centers[CONST_MEM / sizeof(float)] = {1};

__global__ void kernel (int i, float f)
{
    float r = d_cnst_centers[i] * expf(f);
    printf ("r=%15.8f\n", r);
}

int main (void)
{
    kernel<<<1,1>>>(0,25.0f);
    cudaDeviceSynchronize();
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

r=72004902912.00000000这可以很好地编译并在运行时打印。现在让我们expf改为sinf

#include <stdio.h>
#include <stdlib.h>

#define CONST_MEM (65536)
__constant__ float d_cnst_centers[CONST_MEM / sizeof(float)] = {1};

__global__ void kernel (int i, float f)
{
    float r = d_cnst_centers[i] * sinf(f);
    printf ("r=%15.8f\n", r);
}

int main (void)
{
    kernel<<<1,1>>>(0,25.0f);
    cudaDeviceSynchronize();
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

这会在编译期间引发错误: ptxas error : File uses too much global constant data (0x10018 bytes, 0x10000 max)

如果我们使用双精度函数sin,则需要更多常量内存:

#include <stdio.h>
#include <stdlib.h>

#define CONST_MEM (65536)
__constant__ float d_cnst_centers[CONST_MEM / sizeof(float)] = {1};

__global__ void kernel (int i, float f)
{
    float r = d_cnst_centers[i] * sin((double)f);
    printf ("r=%15.8f\n", r);
}

int main (void)
{
    kernel<<<1,1>>>(0,25.0f);
    cudaDeviceSynchronize();
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

我们收到错误消息: ptxas error : File uses too much global constant data (0x10110 bytes, 0x10000 max)