GPU的checker优化——抛砖
最近加班都是因为着急做这玩意,更不动了
为什么加班呢,因为之前做CPU验证的checker现在都得重构了


首先讲一下CPU到GPU在处理并行指令流的区别:
CPU 倾向于利用core_id这一物理信息进行拆分;这里是一个简单的双核CPU利用分支跳转实现多线程并行的伪代码

core0和core1同时收到了这样一段指令流,在PC=2时触发了分支跳转的场景,从而计算出了不同的 r2。
这么做的缺陷在于:
所有的分支都需要出现在汇编指令流内显示控制,多核多线程数量上去以后,过于占用cache;
在多线程场景下,需要频繁地进行分支预测,预测失败的成本很高,效率受影响;
可并行度受限于core的数量,超出core_num的并行分支就需要软件层面重新调度下一轮的branch判断。
GPU的多线程并行并不会考虑执行单元数量(CPU和GPU的执行单元数量级也不一样),执行指令流会被拆分成线程束,选取有空闲的执行单元轮询下发

以上是背景,这里不详细讲解了
那么对于验证过程中的checker添加会有什么影响?
可以参考下面这个图,由于我们的项目中,理想模型来自于功能模拟器,而非性能模拟器(实现成本太高了,没钱没人做),因此模拟器和电路的执行顺序可能出现偏差:因为模拟器并不感知“时间”这个概念,对于模拟器来说,所有线程都是瞬间执行完成的,并不存在“轮询”这个说法。

由此带来的问题就很多了,这里只举例share memory。
由于share memory以线程块为单位进行隔离,每个block的结果都会残存在share memory当中。以往CPU验证中,会在每个kernel执行完后,进行share memory的结果比对(当然也可以在每一笔读写操作时进行比对,会更细致)
前面说到,模拟器是不会轮询的,就是单纯按顺序下发;而RTL则会考虑每个block执行时间长短,选取空闲处理单元执行下一个block,这就导致RTL和模拟器的执行顺序、以及block和执行单元对应关系是不一致的。

以上图为例,针对于某个执行单元,其share memory的操作流是不一样的,结果也不一样
那么checker该怎么加?
一种思路是,在每个block结束时,记录当前的block_id,从模拟器中读取对应block_id的golden数据。这里需要模拟器或者RTL提供当前block操作改写过的地址段信息,或者验证环境自己记录;随后针对改写过的地址进行比对,避免模拟器未操作过的空地址进行比对。
随后golden队列中记录改写过的地址数据,作为下一次比对的“初始状态”。
以此方式按block为单位进行循环比对即可。
同样的,这种比对方式也可以蔓延到寄存器级别,且寄存器级别的比对时间节点可以更细致,以线程束为单位进行。
目前还没想好的一个问题是,原子指令该怎么处理,因为抢锁这个行为也是不可控的。