我正在使用Nvidia驱动程序进行小型OpenCL基准测试, 我的内核执行1024次熔丝乘法 - 并将结果存储在一个数组中:
#define FLOPS_MACRO_1(x) { (x) = (x) * 0.99f + 10.f; } // Multiply-add
#define FLOPS_MACRO_2(x) { FLOPS_MACRO_1(x) FLOPS_MACRO_1(x) }
#define FLOPS_MACRO_4(x) { FLOPS_MACRO_2(x) FLOPS_MACRO_2(x) }
#define FLOPS_MACRO_8(x) { FLOPS_MACRO_4(x) FLOPS_MACRO_4(x) }
// more recursive macros ...
#define FLOPS_MACRO_1024(x) { FLOPS_MACRO_512(x) FLOPS_MACRO_512(x) }
__kernel void ocl_Kernel_FLOPS(int iNbElts, __global float *pf)
{
for (unsigned i = get_global_id(0); i < iNbElts; i += get_global_size(0))
{
float f = (float) i;
FLOPS_MACRO_1024(f)
pf[i] = f;
}
}
但是当我查看生成的PTX时,我看到了这一点:
.entry ocl_Kernel_FLOPS(
.param .u32 ocl_Kernel_FLOPS_param_0,
.param .u32 .ptr .global .align 4 ocl_Kernel_FLOPS_param_1
)
{
.reg .f32 %f<1026>; // 1026 float registers !
.reg .pred %p<3>;
.reg .s32 %r<19>;
ld.param.u32 %r1, [ocl_Kernel_FLOPS_param_0];
// some more code unrelated to the problem
// ...
BB1_1:
and.b32 %r13, %r18, 65535;
cvt.rn.f32.u32 %f1, %r13;
fma.rn.f32 %f2, %f1, 0f3F7D70A4, 0f41200000;
fma.rn.f32 %f3, %f2, 0f3F7D70A4, 0f41200000;
fma.rn.f32 %f4, %f3, 0f3F7D70A4, 0f41200000;
fma.rn.f32 %f5, %f4, 0f3F7D70A4, 0f41200000;
// etc
// ...
如果我是正确的,PTX使用 1026 浮点寄存器来执行1024次操作,并且永远不会重复使用两次寄存器,即使它只能使用2个寄存器执行所有乘加操作。 1026远远高于线程允许的最大寄存器数(根据specs),所以我猜这最终会导致内存溢出。
它是编译器错误还是我完全错过了什么?
我在Quadro K2000 GPU上使用nvcc 6.5版。
修改
实际上我确实错过了规格中的内容:
&#34;由于PTX支持虚拟寄存器,因此生成编译器前端是很常见的 大量的寄存器名称。而不是要求明确声明每个名字, PTX支持用于创建具有公共前缀字符串的变量集的语法 附加整数后缀。例如,假设某个程序使用大量数字 一百个.b32变量,名为%r0,%r1,...,%r99&#34;
答案 0 :(得分:4)
PTX file format旨在描述虚拟机和指令集架构:
PTX为通用并行线程执行定义了虚拟机和ISA。 PTX程序在安装时被转换为目标硬件指令集。 PTX-to-GPU转换器和驱动程序使NVIDIA GPU可用作可编程并行计算机。
因此,您在那里获得的PTX输出不是一种“GPU汇编程序”。它只是一个中间表示,旨在能够描述几乎任何形式的并行计算。
然后将PTX表示编译成相应目标GPU的实际二进制文件。这对于从实际体系结构中抽象是很重要的 - 特别是关于您的示例:应该可以使用程序的相同的 PTX表示,无论如何特定目标计算机上可用的寄存器数量。您看到的1026“寄存器”是“虚拟”寄存器,最后可以映射到实际可用的(少数)实际硬件寄存器。您可以在编译期间将--ptxas-options=-v
参数添加到NVCC,以获取有关寄存器使用情况的附加信息。
(这与LLVM背后的概念大致相同 - 即,有一个可以优化和争论的表示,两者都从原始源代码和中抽象出来来自实际的目标架构)。