剖析NVIDIA Volta架构之指令篇

2019-05-28 18:32:49 浏览数 (1)

写在最前

由于实验结果不太好,现在已经开始往最底层的sass修改上努力了,鉴于nvidia官方出于大概是商业目的,关于sass的内容少之又少,因此只能零星地从各种paper或者之类的东西里寻找。前两天发现了一个文档,是关于Volta架构的,里面讲了一些关于sass的内容,大致和 maxas 的介绍差不多但是更好懂,特此翻译了相关部分,也就是第二章的内容。

ps : 点此下载原文

正文开始

Volta使用的指令编码方式与Pascal和Maxwell架构不同。

与以前架构最为不同的地方就是,Volta使用128位来编码每个指令和指令对应的控制信息。以前的架构都是使用64位编码每个指令,然后再分出额外的64位来表示控制信息,而一条控制信息可能是和多条指令相关联的。下面是个相关的例子:

以上代码是使用nvdisasm反汇编出来的,其分成两个64位用于方便显示,第一行只是编码后的指令信息,第二行包含指令信息和控制信息。

据我们从彻底的汇编指令中得到的知识,这128位是按照如下规则划分的:

  • 至少有91位用于指令编码
  • 至少23位用于控制信息
  • 据我们实验来看,剩下的14位没用

控制信息

Kepler架构将控制信息引入了编译器对于指令的编码调度过程。控制信息能够防止数据冲突并且允许简单的片上逻辑,这使GPU获得更高的计算密度和更低能耗

在Volta上,128位包括指令和指令相关的控制信息

Volta之前的架构都是一条控制信息和多条指令相连(在Pascal和Maxwell里是3条,Kepler里是7条)。每一条控制信息表示了与它相关的这几条指令的调度方式。下面的代码就是Pascal架构下的示例,一共包括4个64位字,第一个64字只有十六进制的表示而没有对应的指令,就是控制字段;而余下的三个就是指令。

代码语言:javascript复制
                                                /* 0x000f8800fe2007f1 */ 
/*0288*/    @P5 LDG.E.CI R66, [R86 0x100];      /* 0xeed4a00010055642 */
/*0290*/    @!P5 MOV R66, RZ;                   /* 0x5c9807800ffd0042 */
/*0298*/    @P6 LDG.E.CI R67, [R86 0x180];      /* 0xeed4a00018065643 */

控制信息在不同的架构上编码方式不同,具体如下:

  • Kepler架构,包括最高有效位的6个0和最低有效位的2个0,以及7个部分,每个部分8位
  • Pascal和Maxwell架构,包括最高位的一个0和三个部分,每个部分21位
  • Volta架构,包括最高有效位的两个0和一个21位的部分。每一个128位字开头都是控制信息,后跟着指令的编码。

Volta,Pascal和Maxwell架构中的控制信息的组织方式都是一致的,每个部分包括6个编码域,如下所示:

其各自的意义如下:

  1. 重用标志 Volta,Pascal和Maxwell有4个寄存器重用cache和4个源操作数槽(slot)。这四位每一位都和一个8bytes的槽相连。当设置了重用标志之后,与之相连的寄存器值就会被存入寄存器重用cache中,给可能再次使用这个寄存器的指令使用。重用还能减少寄存器的存储体冲突(bank conflict)。最低有效位代表第一个源操作数槽,最高有效位代表第四个源操作数槽
  2. 等待栅(barrier)掩码;读写栅标记 虽然大多数指令执行的时间是固定的并且能够被汇编器静态调度,但是那些包含访存和共用计算资源的指令的执行时间则是变化的。Volta,Pascal和Maxwell使用“依赖栅”来测定这些可变时延的指令的完成时间并解决数据冲突。通过设置”write barrier number”,当一个可变时延的指令写入一个寄存器时,汇编器将其和一个栅相连。当这个指令之后的某个指令想要访问这个写入的寄存器时,通过设置”wait barrier mask”中与对应栅相关的位,汇编器就将这个指令设置为等待当前栅完成。硬件就会让这个指令被阻塞直到它需要的内容准备好。一个指令可能需要等待多个栅的完成,这就是为什么等待栅不是简单的标记而是掩码的原因。
  3. 读依赖栅 读依赖栅是用来解决读后写的问题。没有缓冲区的指令在从寄存器读取数写入内存的时候,需要寄存器的数值不变。为了保证这个,汇编器通过设置”read barrier number”将其和一个栅绑定。之后想要写入这个寄存器的指令要等待这个栅的完成。
  4. 阻塞延迟 这个四位的域表示调度器在执行下一个命令前需要等待的时间,范围是0-15.在Pascal和Maxwell架构中,如果这个域和“yeild flag”的组合表示包含特定额位,就会使得一个运算块中的两个分发器同时发出一个两个连续的指令,被称为”dual issue“。在Volta架构中只有一个分发器,所以并没有发现这种双重发出的情况。
  5. 域标记 Volta使用域标记来平衡分配给处理块的任务量。当这个标记位被设置,调度器就更倾向于发出当前warp的指令,如果没有被设置,调度器就倾向于替换成别的warp,并使得所有的寄存器重用标志都失效。如果切换成别的warp,会消耗额外的一个时钟周期。

调度器

Volta的SM被分成了四个处理块。相同warp上的指令会被分配给一个特定的块,并且只能使用这一个块内的计算资源。warp和(处理块中)调度器的映射关系就是 scheduler_id = warp_id%4,为了证明它,我们进行了实验(实验略)

指令编码

相比于以前的架构,Volta使用更多的位编码指令。

和以前那些将操作指令放在最高有效位的架构(Pascal,Maxwell和Kepler)不同的是,Volta将操作指令放在第一个64位字的最低有效位。我们在附录中展示了Pascal和Volta的编码。

Volta的操作码的长度是10-13位。

和以前的架构一样,Volta的操作可以是寄存器(通用,特殊或者断言),内存地址(常量,共享或者全局)。断言由4位表示:第一位是有效位,剩下三位是一个断言寄存器的编号。

0 人点赞