前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >剖析NVIDIA Volta架构之指令篇

剖析NVIDIA Volta架构之指令篇

作者头像
用户1148523
发布2019-05-28 18:32:49
5860
发布2019-05-28 18:32:49
举报
文章被收录于专栏:FishFish

写在最前

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

ps : 点此下载原文

正文开始

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

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

Volta指令编码
Volta指令编码

以上代码是使用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位表示:第一位是有效位,剩下三位是一个断言寄存器的编号。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年05月07日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在最前
  • 正文开始
    • 控制信息
      • 调度器
        • 指令编码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档