“FPGA vs ASIC,孰强孰弱?” 这是在我心中埋藏很久的一个疑问。因为听到有言论说在 DNN 上,FPGA 被 ASIC 完爆,能耗和面积都不占优势;而又听到 FPGA 在其他比如量化领域仍有重要的应用空间。我好奇究竟什么情况下 FPGA 会更有优势呢?FPGA 相比 ASIC 的区别无非便是可重构能力,没有数据无法结合具体场景自顶向底推,便从底出发整理可重构计算的特点。
时空域的划分
《可重构计算》[1] 中对可重构计算定义如下:
时空域并行计算 这个名词总结很 high-level 描述非常符合硬件思维,毕竟处理是数据沿着时间流过空间,处理器就是能执行处理的硬件,在之前blog中也提到 pipeline 是一种时域并行技术[2]。但具体如何严谨定义时域空域呢?查询文献无果,我尝试结合自己理解对其重定义。
结合上图,我们可以看到时空域的划分和时域硬件高度相关。也就是说我要先定义一个时域处理器,然后才能对计算划分时空域,这是一个相对的概念。比如图中传统处理器是时域计算,对于 \(y=A\times x\times x+B\times x+C\) 这个计算,划分成 5 条指令,在时间上用 5 个周期执行。那么这里计算的最小粒度就是如(a)的指令,(b)可以同时执行多个最低粒度计算,那么便是空域计算。假设问题1我计算2次,那么(b)处理器也需要俩个周期,此时若把问题1的粒度设置为最低粒度,那么(b)也成了时域计算了。
所以:时空域是相对的概念,由指定的一个时域处理器所定义。该时域处理器每时刻处理的计算粒度便是时域计算的粒度, 再换言之,定义时空域一定需要一个 baseline。
“横看成岭侧成峰,远近高低各不同。”
不同计算分类
定义时空域后,还有一个概念没有解释清楚,什么是并行?并行脑海中直观想多多个处理器同时处理的画面,从一个变多个(一定要有 baseline),这是很直观的空域并行。但又提到 pipeline 是一种时域并行技术,这又怎么理解呢?
接下来考虑三种情况
- 单线程 CPU -> 多线程 CPU / GPU
- 非流水线硬件 -> 流水线硬件
- 非可重构计算硬件 -> 可重构计算硬件
第一个情形好理解,但注意这并不是一个无脑叠规格的情形,变换前后的面积开销可以保持不变 [3]而增加处理器数量。第二个的 baseline 应当是该有的硬件还存在,比如经典的五级流水线该有的功能都在,但不是 pipeline 执行,同一时刻只有一级单元激活。情形三中存在俩种不同的算力需求算力A和算力B,非可重构硬件中存在一个硬件A和一个硬件B来分别处理,而可重构硬件后只有一个硬件 C ,通过重构执行算力计算 A 和 B,即上图中(b)到(c)中的变化。
我们把情形1,2变换前的 baseline 定义为时域处理器,情形3中 A或B 的计算粒度定义为时域粒度(为何这么定义后面解释),那么三种计算范式变换如下:
- 单线程 CPU -> 多线程 CPU / GPU (时域 -> 空域)
- 非流水线硬件 -> 流水线硬件 (时域 -> 空域)
- 非可重构计算硬件(1个A一个B) -> 可重构计算硬件(一个C) (时域/空域 -> 时域)
因为选择的 baseline 不一样,此时的可重构计算定义从时空域变成了时域计算。
为什么非可重构计算硬件是时域 或 空域呢,这和具体的数据相关关系有关。因为我们是以A或B的粒度定义为时域最小粒度,假如 A 和 B 相关,B 必须等 A 执行完,那么可以想象在非重构计算硬件上硬件A和硬件B的调度时间不重叠,是时域处理器;假如二者可以同时执行,那么该硬件便成了空域处理器了。而可重构硬件由于只有一个硬件 C,必须时间上隔开分别执行 A、B 计算,是时域处理器。
进一步分析,如果原本非可重构硬件是时域计算,变为可重构计算后,面积减少,性能不变;而若非可重构计算是空域计算,变为可重构计算后,面积减少,性能减少。再进一步考虑 scale-up,如果将可重构硬件扩展到俩个C,那么对于前者是面积不变,性能增加;后者是面积不变,性能不变。以上情形假设重构成本为0,进一步修正则要将其考虑进来重新评估。
- 非可重构计算硬件(一个A一个B) -> 可重构计算硬件(俩个C,但同时配置成俩个A或俩个B)
- 时域 -> 时空域:面积不变,性能增加
- 空域 -> 空域:面积不变,性能不变(考虑重构时空成本,则是面积增加,性能减少)
以上分析可见,可重构计算只当原本硬件 utilization 很低的情况下有一定优势,如果原本硬件 utilization 很高,考虑重构带来的额外成本此时反而性能更差。
比如有一个场景要训 Transformer 模型,那么集群全部都是 Transformer-ASIC,因为对 Transformer 特殊优化,utilization 非常高,此时硬件便没有可重构介入的空间。如果一个场景有 Transformer、CNN 以及一些乱七八糟的模型,导致此时硬件虽然可以计算,但执行其他模型时 utilization 就不高了,便可让可重构发光发热了。
以上分析还是很笼统,实际上即使相同模型对算力的需求也不一样的,模型规格和拓扑也会影响在硬件上的具体实现。但可以看出,utilization 很低来自于场景的复杂性,而原本硬件没有处理复杂性能力,导致 utilization 退化。所以想要挖掘可重构计算能力,一是要找到某个复杂场景,而是 profile 该场景下 utilization 到底退化多少,接着才是评估是否可重构以及重构的成本。
软硬件协同设计——软件为主硬件为次
上文解释了什么情况下可重构会有优势,但还没有解释什么时候能够可重构,这要可以从软件角度做更多挖掘分析。
我把软件定义为一些算子按照相关/依赖关系组成的计算图。这有俩个概念,一是基础粒度是算子,其次是依赖关系,这个概念也是相对概念,一些小算子可以合成一个大算子考虑,取决思考的坐标系。
那么可重构首先就要保证算子功能之间的切换是合理的,这和具体重构粒度以及算子之间的相似度有关,需要具体分析,假设已经分析得到结论。
接下来是依赖关系,如果原本在时域上执行的软件划分到空域执行,就要考虑解耦依赖关系,才能并行计算。所以单线程到多线程的时空域变换依赖于 Amdahl's 定律中并行部分占比,非 pipeline 到 pipeline 取决于同一指令不同 stage 之间的依赖关系,不同 stage 的依赖固然存在引发冒险,但 RISC 指令集就比 CISC 更好解耦依赖从而流水化。
对于可重构硬件,发挥优势有俩种可能,一是维持硬件规格,单纯减少面积维持性能,时域->时域,此时只需考虑算子功能的约束;二是增大硬件规格,面积不变增大性能,时域->时空域,此时需同时考虑算子和依赖关系的约束。
我之前一直持有一个观点,即并行和依赖这俩个概念是相互依存的 [4],如果用时空域的概念解释,便是从时域计算到空域计算范式的变化。