关于加载/存储指令有两个nvprof
指标,它们是ldst_executed
和ldst_issued
。我们知道executed<=issued
.我希望那些已发出但未执行的负载/存储与分支预测和其他不相关的预测有关。但是,从本文档(幻灯片 9)和本主题中,发出但未执行的指令与序列化和重播相关。
我不知道这个原因是否适用于加载/存储说明。此外,我想知道为什么这些术语用于发出但不执行的指令?如果出于任何原因进行序列化,则会多次执行指令。那么,为什么它们不算executed
?
对此有什么解释吗?
NVIDIA 架构通过为一组称为 warp 的线程发出指令来优化内存吞吐量。如果每个线程访问一个连续的数据元素或相同的元素,则可以非常有效地执行访问。但是,如果每个线程访问不同缓存行或同一库中不同地址中的数据,则存在冲突,必须重放指令。
inst_executed是停用的指令计数。 inst_issued是发出的指令计数。在矢量存储器访问、存储器地址冲突、存储器库冲突等情况下,一条指令可能会多次发出。在每个问题上,线程掩码都会减少,直到所有线程都完成。
做出这种区分有两个原因: 1. 指令的停用表示数据依赖性的完成。尽管可能重播,但数据依赖项仅解析 1 次。 2. 发出和执行之间的比率是显示节省 warp 调度程序问题周期的机会的简单方法。
在费米和开普勒SM中,如果遇到内存冲突,则重放(重新发出)指令,直到所有线程完成。这是由扭曲调度程序执行的。这些重播会消耗问题周期,从而降低 SM 向数学管道发出指令的能力。在此SM中,发出>执行表示优化的机会,尤其是在发出的IPC很高的情况下。
在用于矢量访问的麦克斯韦-图灵 SM 回放中,地址冲突和内存冲突由内存单元(共享内存、L1 等)重放,并且不会窃取 warp 调度程序问题周期。在此SM中,发布的SM很少超过执行的几个百分点。
示例:内核加载 32 位值。warp 中的所有 32 个线程都处于活动状态,每个线程访问一个唯一的缓存行(步幅 = 128 字节)。
在开普勒 (CC3.*) SM 上,指令发出 1 次,然后重放 31 次,因为开普勒 L1 每个请求只能执行 1 次标签查找。
inst_executed = 1 inst_issued = 32
在开普勒上,对于L1中错过的每个请求,必须再次重放指令。如果 L1 缓存中的所有线程都未命中,则
inst_executed = 1 inst_issued>= 64 = 32 次请求 + 32 次失误重放
在麦克斯韦-图灵架构上,重放由SM内存系统执行。重播可以限制内存吞吐量,但不会阻止 warp 调度程序向数学管道发出指令。
inst_executed = 1 inst_issued = 1
在Maxwell-Turing Nsight Compute/Perfworks上,每个内存管道的吞吐量计数器公开,包括由于内存库冲突,原子学序列化,地址分歧等导致的周期数。
GPU 架构基于最大化吞吐量而不是最小化延迟。因此,GPU(目前)并没有真正执行乱序执行或分支预测。GPU 不是构建几个充满复杂控制逻辑的内核来使一个线程运行得非常快(就像你在 CPU 上那样),而是使用这些晶体管来构建更多内核来并行运行尽可能多的线程。
如您链接的演示文稿的幻灯片 9 所述,执行指令是控制流在程序中传递的指令(基本上是运行的汇编代码行数)。例如,当您执行全局加载指令并且无法立即提供内存请求(错过缓存)时,GPU 将切换到另一个线程。一旦值在缓存中准备就绪并且 GPU 切换回您的线程,就必须再次发出加载指令以完成获取值(另请参阅此答案和此线程)。例如,当您访问共享内存并且存在银行冲突时,必须为扭曲中的不同线程多次重放共享内存访问......
区分已执行指令和已发出指令的主要原因似乎是,两者的比率可以作为代码产生的开销的度量,因为指令在执行时无法立即完成......