多处理器级别:
在更低层次上,应用程序应该最大化多处理器内各个功能单元之间的并行执行;
如“硬件多线程”中所述,GPU多处理器依赖于线程级并行性来最大限度地利用其功能单元。因此利用率与驻留经线的数量直接相关。在每个指令发布时间,一个warp调度器选择一个准备好执行下一个指令的warp(如果有的话),然后发送指令给warp的活动线程。一个warp准备好执行下一条指令需要的时钟周期数称为等待时间,当所有的warp调度器总是有一些指令在每个时钟周期发出一些warp时,或者换句话说,当延迟完全“隐藏”时。隐藏L个时钟周期的延迟所需的指令数取决于这些指令的各自吞吐量(请参阅算术指令了解各种算术指令的吞吐量)。假设所有指令的吞吐量最大,计算能力3.x的设备的吞吐量为8L,这是因为多处理器每次在四个warp的一个时钟周期内每个warp发出一对指令,如Compute Capability 3.x中所述。
对于计算能力3.x的设备,每个周期发布的八条指令是四对不同的warp,每对是为同一个warp。
一个warp没有准备好执行下一条指令的最常见的原因是指令的输入操作数还没有可用。
如果所有输入操作数都是寄存器,则延迟是由寄存器依赖性引起的,即一些输入操作数是由一些先前的指令执行还没有完成的。 在背靠背寄存器依赖性的情况下(即,一些输入操作数是由前一条指令写入的),延迟等于前一条指令的执行时间,并且变形调度器必须在该变形期间 时间。 执行时间依赖于指令而变化,但是对于计算能力3.x的设备来说通常大约是11个时钟周期,对于计算能力3.x的设备来说,这转换成44个warp(假设warp执行吞吐量最大的指令,否则更少 需要warp)。 这也假定了足够的指令级并行性,以便调度程序总是能够为每个warp发出指令对。
如果某些输入操作数驻留在片外存储器中,则计算能力3.x的设备的延迟要高得多:200至400个时钟周期。 在如此高的等待时间内保持warp调度器繁忙所需的warp数量取决于内核代码及其指令级并行度。 一般来说,如果不带片外存储器操作数的指令(即,大多数时间的算术指令)的数量与带有片外存储器操作数的指令的数量的比率低(通常这个比率是常数 称为程序的算术强度)。 例如,假设这个比率是30,同时假设计算能力3.x的设备上的延迟是300个周期。 然后计算能力3.x的设备需要大约40个warp(假设与前一段相同)。
另一个原因是warp没有准备好执行下一条指令,它正在等待某个内存栏(Memory Fence Functions)或同步点(Memory Fence Functions)。 同步点可以强制多处理器空闲,因为越来越多的经线等待同一个块中的其他经线在同步点之前完成指令的执行。 在这种情况下,每个多处理器有多个驻留块可以帮助减少怠速,因为来自不同块的经线不需要在同步点处等待彼此。
对于给定的内核调用,驻留在每个多处理器上的块和warp的数量取决于调用的执行配置(Execution Configuration),多处理器的内存资源以及硬件多线程中描述的内核的资源需求。 使用-ptxas-options = -v选项进行编译时,编译器会报告注册和共享内存使用情况。
块所需的共享内存总量等于静态分配的共享内存量和动态分配的共享内存量之和
内核使用的寄存器数量可能会对驻留经纱的数量产生重大影响。 例如,对于计算能力6.x的设备,如果一个内核使用64个寄存器,每个块有512个线程并且只需要很少的共享内存,则两个块(即32个变形)可以驻留在多处理器上,因为它们需要2x512x64寄存器 ,与多处理器上可用寄存器的数量完全一致。 但是一旦内核使用一个寄存器,只有一个块(即16个warp)可以驻留,因为两个块将需要2×512×65个寄存器,这些寄存器比多处理器上可用的寄存器多。 因此,编译器会尽量减少寄存器使用量,同时保持寄存器溢出(请参阅“设备内存访问”),并尽量减少指令数量。 可以使用maxrregcount编译器选项或启动界限来控制注册表使用情况,如启动界面中所述。
每个double变量和每个长变量使用两个寄存器。
执行配置对于给定内核调用的性能的影响通常取决于内核代码。 因此建议进行实验。 应用程序还可以根据寄存器文件大小和共享内存大小(取决于设备的计算能力)以及设备的多处理器数和内存带宽来参数化执行配置,所有这些都可以使用运行时查询 (参见参考手册)。
应该选择每个块的线程数作为翘曲大小的倍数,以避免尽可能多地浪费带有不足的歪曲的计算资源。