在CUDA代码中意外地使用了很大的cmem [2]

时间:2012-10-17 20:57:27

标签: optimization cuda gpu

我正在CUDA上编写一个(公认的本地内存密集型)代码,并且已经过了开发阶段并加速了。命令行分析器指示我的占用率(我认为)非常低(主要内核为0.083 - 0.417),我想改进这一点。不幸的是,计算工作需要大量的__shared__内存(每128个线程块16-20 kB)和一些寄存器(主要内核报告为63,尽管我不确定我是否真的使用了这个许多...)

我真正的问题是,与cmem[2]有关。这是一个例子:

  

使用了8个寄存器,40个字节的cmem [0],51584个字节的cmem [2],368个字节的cmem [14],4个字节的cmem [16]

所有内核似乎都使用了大量的cmem[2],我甚至不确定它是什么。我通过常规cudaMalloc调用在设备上存储了大量内存,并通过__constant__调用在cudaMemcpyToSymbol内存中存储了少量双精度和指针(尽管接近50 kB),但这是关于它。所以我的问题是:我究竟能使用所有这些cmem[2],它是否限制了我的内核占用率?

另外,我正在运行带有CUDA 4.2和Ubuntu 10.04.4 64位的GTX 550 Ti。 nvcc制作的可执行文件包含在mpirun中,因为代码也是MPI并行化的。

1 个答案:

答案 0 :(得分:3)

在Fermi架构上,CUDA驱动程序使用cmem [2]存储常量变量。同一模块中的所有函数共享相同的常量。这个恒定银行的大小不会影响您的理论SM占用率或增加您的启动费用。如果超过最大大小(64KB),则应收到编译器错误。

CUDA二进制实用程序cuobjdump可用于调试分配。

如果您的文件sm20.cu包含以下常量

__constant__ float k_float_array[] = { 0.f, 1.f, 2.f, 3.f };
__constant__ double k_double_array[] = { 0.0, 1.0, 2.0, 3.0 };
__constant__ int k_int_array[] = { 0, 1, 2, 3 };

__global__ void empty_kernel(float* a)
{
    return;
}

你可以运行

cuobjdump.exe -elf sm20.cu.obj

可执行文件也可以用作参数。运行cuobjdump --help以获取选项列表。

此命令将产生以下输出

Fatbin elf code:
================
arch = sm_20
code version = [1,5]
producer = cuda
host = windows
compile_size = 32bit
identifier = c:/dev/constant/sm20.cu

32bit elf: abi=5, sm=20, flags = 0x140114
Sections:
Index Offset   Size ES Align   Type   Flags Link     Info Name
    1     34     a6  0  1    STRTAB       0    0        0 .shstrtab
    2     da     e9  0  1    STRTAB       0    0        0 .strtab
    3    1c4     80 10  4    SYMTAB       0    2        6 .symtab
    4    244     18  0  4 CUDA_INFO       0    3        0 .nv.info
    5    25c     20  0  4 CUDA_INFO       0    3        8 .nv.info._Z12empty_kernelPf
    6    27c     24  0  4  PROGBITS       2    0        8 .nv.constant0._Z12empty_kernelPf
    7    2a0     40  0  8  PROGBITS       2    0        0 .nv.constant2
    8    2e0     10  0  4  PROGBITS       6    3  2000007 .text._Z12empty_kernelPf

精灵部分.nv.constant2包含cmem [2]的内容。这个大小是0x40 == 64字节,符合我的期望。

.nv.constant2是部分索引7。

.section .strtab

.section .shstrtab

.section .symtab
 index     value     size      info    other  shndx    name
   0          0        0        0        0      0     (null)
   1          0        0        3        0      8     .text._Z12empty_kernelPf
   2          0        0        3        0      6     .nv.constant0._Z12empty_kernelPf
   3          0        0        3        0      7     .nv.constant2
   4          0       16        1        0      7     k_float_array
   5         16       32        1        0      7     k_double_array
   6         48       16        1        0      7     k_int_array
   7          0       16       12       10      8     _Z12empty_kernelPf

.symtab包含定义的3个常量数组。所有带shndx == 7的符号都在.nv.constant2

.nv.constant0._Z12empty_kernelPf
0x00000000  0x00000000  0x00000000  0x00000000  0x00000000
0x00000000  0x00000000  0x00000000  0x00000000

.nv.constant2
0x00000000  0x3f800000  0x40000000  0x40400000  0x00000000
0x00000000  0x00000000  0x3ff00000  0x00000000
0x40000000  0x00000000  0x40080000  0x00000000
0x00000001  0x00000002  0x00000003

.nv.constant2定义了该部分中的二进制数据。这与声明的常量变量匹配。如果你有很多常量,.symtab部分标识每个符号的偏移量和大小。

// skipping .nv.info and .text

cuobjdump可用于转储PTX和SASS代码。 PTX和SASS程序集可用于确定哪些内核使用常量。