【问题标题】:cuda - minimal example, high register usagecuda - 最小的例子,高寄存器使用率
【发布时间】:2013-06-17 10:47:49
【问题描述】:

考虑这 3 个微不足道的最小内核。他们的寄存器使用率比我预期的要高很多。为什么?

答:

__global__ void Kernel_A()
{  
//empty
}

对应的ptx:

ptxas info    : Compiling entry function '_Z8Kernel_Av' for 'sm_20'
ptxas info    : Function properties for _Z8Kernel_Av
    0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 2 registers, 32 bytes cmem[0]

乙:

template<uchar effective_bank_width>
__global__ void  Kernel_B()
{
//empty
}

template
__global__ void  Kernel_B<1>();

对应的ptx:

ptxas info    : Compiling entry function '_Z8Kernel_BILh1EEvv' for 'sm_20'
ptxas info    : Function properties for _Z8Kernel_BILh1EEvv
    0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 2 registers, 32 bytes cmem[0]

C:

template<uchar my_val>
__global__ void  Kernel_C
        (uchar *const   device_prt_in, 
        uchar *const    device_prt_out)
{ 
//empty
}

对应的ptx:

ptxas info    : Compiling entry function '_Z35 Kernel_CILh1EEvPhS0_' for 'sm_20'
ptxas info    : Function properties for _Z35 Kernel_CILh1EEvPhS0_
    16 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas info    : Used 10 registers, 48 bytes cmem[0]

问题:

为什么 empty 内核 A 和 B 使用 2 个寄存器? CUDA 总是使用一个 implicit 寄存器,但是为什么要使用另外 2 个 explicit 寄存器呢?

内核 C 更令人沮丧。 10个寄存器?但是只有2个指针。这为指针提供了 2*2 = 4 个寄存器。即使有另外 2 个神秘的寄存器(由内核 A 和内核 B 建议),这将给出总共 6 个。 仍然远小于 10 !


如果您有兴趣,这里是内核 A 的 ptx 代码。内核 B 的 ptx 代码完全相同,以整数值和变量名称为模。

.visible .entry _Z8Kernel_Av(    
)
{           
        .loc 5 19 1
func_begin0:
        .loc    5 19 0

        .loc 5 19 1

func_exec_begin0:
        .loc    5 22 2
        ret;
tmp0:
func_end0:
}

对于内核 C...

.weak .entry _Z35Kernel_CILh1EEvPhS0_(
        .param .u64 _Z35Kernel_CILh1EEvPhS0__param_0,
        .param .u64 _Z35Kernel_CILh1EEvPhS0__param_1
)
{
        .local .align 8 .b8     __local_depot2[16];
        .reg .b64       %SP;
        .reg .b64       %SPL;
        .reg .s64       %rd<3>;


        .loc 5 38 1
func_begin2:
        .loc    5 38 0

        .loc 5 38 1

        mov.u64         %SPL, __local_depot2;
        cvta.local.u64  %SP, %SPL;
        ld.param.u64    %rd1, [_Z35Kernel_CILh1EEvPhS0__param_0];
        ld.param.u64    %rd2, [_Z35Kernel_CILh1EEvPhS0__param_1];
        st.u64  [%SP+0], %rd1;
        st.u64  [%SP+8], %rd2;
func_exec_begin2:
        .loc    5 836 2
tmp2:
        ret;
tmp3:
func_end2:
}
  1. 为什么要先声明一个本地内存变量(.local)?
  2. 为什么两个指针(作为函数参数给出)存储在寄存器中?他们没有特殊的参数空间吗?
  3. 也许这两个函数参数指针属于寄存器 - 这解释了两个 .reg .b64 行。但是.reg .s64 行是什么?为什么会在那里?

情况变得更糟:

D:

template<uchar my_val>
__global__ void  Kernel_D
        (uchar *   device_prt_in, 
        uchar *const    device_prt_out)
{ 
    device_prt_in = device_prt_in + blockIdx.x*blockDim.x + threadIdx.x;
}

给予

ptxas info    : Used 6 registers, 48 bytes cmem[0]

所以操作参数(指针)从 10 个寄存器减少到 6 个?

【问题讨论】:

    标签: optimization assembly cuda gpu ptx


    【解决方案1】:

    首先要说明的是,如果您担心寄存器,请不要查看 PTX 代码,因为它不会告诉您任何信息。 PTX 使用静态单一赋值形式,编译器发出的代码不包含任何创建可运行机器代码入口点所需的“装饰”。

    说完这些,让我们看看内核 A:

    $ nvcc -arch=sm_20 -m64 -cubin -Xptxas=-v null.cu 
    ptxas info    : 0 bytes gmem
    ptxas info    : Compiling entry function '_Z8Kernel_Av' for 'sm_20'
    ptxas info    : Function properties for _Z8Kernel_Av
        0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
    ptxas info    : Used 2 registers, 32 bytes cmem[0]
    
    $ cuobjdump -sass null.cubin 
    
        code for sm_20
            Function : _Z8Kernel_Av
        /*0000*/     /*0x00005de428004404*/     MOV R1, c [0x1] [0x100];
        /*0008*/     /*0x00001de780000000*/     EXIT;
            .............................
    

    有你的两个寄存器。空内核不会产生零指令。

    除此之外,我无法重现您所展示的内容。如果我查看您发布的内核 C,我会得到这个(CUDA 5 版本编译器):

    $ nvcc -arch=sm_20 -m64 -cubin -Xptxas=-v null.cu 
    ptxas info    : 0 bytes gmem
    ptxas info    : Compiling entry function '_Z8Kernel_CILh1EEvPhS0_' for 'sm_20'
    ptxas info    : Function properties for _Z8Kernel_CILh1EEvPhS0_
        0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
    ptxas info    : Used 2 registers, 48 bytes cmem[0]
    
    
    $ cuobjdump -sass null.cubin 
    
    code for sm_20
        Function : _Z8Kernel_CILh1EEvPhS0_
    /*0000*/     /*0x00005de428004404*/     MOV R1, c [0x1] [0x100];
    /*0008*/     /*0x00001de780000000*/     EXIT;
        ........................................
    

    即。与前两个内核相同的 2 个寄存器代码。

    内核 D 也是如此:

    $ nvcc -arch=sm_20 -m64 -cubin -Xptxas=-v null.cu 
    ptxas info    : 0 bytes gmem
    ptxas info    : Compiling entry function '_Z8Kernel_DILh1EEvPhS0_' for 'sm_20'
    ptxas info    : Function properties for _Z8Kernel_DILh1EEvPhS0_
        0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
    ptxas info    : Used 2 registers, 48 bytes cmem[0]
    
    $ cuobjdump -sass null.cubin 
    code for sm_20
        Function : _Z8Kernel_DILh1EEvPhS0_
    /*0000*/     /*0x00005de428004404*/     MOV R1, c [0x1] [0x100];
    /*0008*/     /*0x00001de780000000*/     EXIT;
        ........................................
    

    同样,2 个寄存器。

    作为记录,我使用的 nvcc 版本是:

    $ nvcc --version
    nvcc: NVIDIA (R) Cuda compiler driver
    Copyright (c) 2005-2012 NVIDIA Corporation
    Built on Fri_Sep_28_16:10:16_PDT_2012
    Cuda compilation tools, release 5.0, V0.2.1221
    

    【讨论】:

    • 我从编译器标志中删除了调试“-G”和“-g”...然后我得到了与内核 C 相同的输出。
    • 我不敢相信。真的是这样吗?
    • 看起来是这样。同样,PTX 不会告诉您您想知道什么 - 调试器支持会导致汇编器发出更多设置代码。这可能就是额外寄存器的来源。
    • 非常好。一般来说,您是否知道一种跟踪寄存器使用情况的方法(通过特定的代码行)?查看汇编代码很困难。我也尝试过运行cuda-gdb 并在某些点打印所有寄存器。但这些很烦人。分析工具仅提供摘要统计信息 - 但我希望对代码行进行微观管理...
    • 真的没有办法做到这一点。编译器和汇编器都具有非常 积极的优化器,可以将输入的 C 代码以 1:1 的比例映射到机器代码,这介于非常困难和几乎是徒劳的之间。如果你想“微观管理”那么直接编写你自己的机器代码,否则你可能会浪费你的时间并寻找错误的地方进行性能优化。而且我非常怀疑你是否能够获得比编译器更好的指令吞吐量或性能。
    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 2012-08-25
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    相关资源
    最近更新 更多