3、指令集设计哲学:RISC vs CISC,SIMD vs SIMT,VLIW与脉动阵列架构的选择

各位同学,今天我们聊点硬核的。指令集设计,说白了就是给NPU定规矩——告诉它怎么干活、干多少活、活干得漂不漂亮。我做了十几年芯片,见过太多团队在架构选型上翻车。嗯,今天我把这些坑都摊开来讲。

3.1 RISC vs CISC:精简还是复杂?这是个问题

先说说RISC和CISC。这两个老冤家从CPU时代就掐到现在。RISC(精简指令集)主张每条指令干一件简单的事,比如加个法、存个数。CISC(复杂指令集)则相反,一条指令能包揽读取、计算、写回全套流程。

我个人习惯在NPU里用RISC风格。为什么?你想想看,神经网络的计算模式非常规整——卷积、池化、激活,翻来覆去就那几招。用复杂指令去封装这些操作,反而把硬件调度搞僵化了。

核心区别:

  • RISC:指令长度固定(通常32位),硬件解码简单,适合流水线深度优化
  • CISC:指令长度可变,一条指令能完成多步操作,但硬件复杂度飙升

我在项目中遇到过一件事:某团队用CISC风格设计了一条「卷积+激活+池化」三合一的指令。听起来很美对吧?结果发现,当卷积核尺寸变化时,这条指令的微码要重写,硬件调度器直接卡死。后来我们改成RISC风格,拆成三条独立指令,用微码序列组合,灵活性瞬间拉满。

避坑指南:我曾经以为CISC能减少指令数、提升效率。后来发现,在NPU里,指令的灵活性比单条指令的「威力」更重要。RISC的模块化设计,让你能轻松应对不同网络结构。

3.2 SIMD vs SIMT:数据并行,但方式不同

接下来是SIMD和SIMT。这两个都是搞数据并行的,但路子完全不同。

SIMD(单指令多数据):一条指令,同时操作多个数据。比如一条加法指令,同时对16个32位浮点数做加法。硬件上就是一组ALU(算术逻辑单元)共享同一个控制单元。

SIMT(单指令多线程):一条指令,驱动多个线程执行。每个线程有自己的寄存器、程序计数器,但共享指令流。NVIDIA的CUDA就是典型代表。

特性 SIMD SIMT
控制单元 共享一个 每个线程独立
数据宽度 固定(如128位、256位) 可变(取决于线程数)
分支处理 所有元素必须走同一分支 每个线程可独立分支(但会降低效率)
典型应用 CPU的向量扩展(如AVX) GPU的通用计算

说白了,SIMD适合数据规整、无分支的场景——比如矩阵乘法、卷积。SIMT则能处理更复杂的控制流,比如稀疏计算、动态形状的网络。

我建议:如果你的NPU主要跑CNN这种规整网络,SIMD就够了。但如果你要支持Transformer、GNN这类有动态稀疏性的模型,SIMT的灵活性会让你少掉很多头发。

注意:SIMT不是免费的午餐。每个线程独立寄存器意味着面积和功耗会飙升。我曾经评估过一个SIMT方案,光寄存器文件就占了芯片面积的30%。嗯,老板当场脸就绿了。

3.3 VLIW vs 脉动阵列:两种极端的并行哲学

最后聊聊VLIW和脉动阵列。这两个代表了指令级并行和架构级并行的两个极端。

VLIW(超长指令字):把多条不相关的指令打包成一条超长指令,由编译器负责调度。硬件只需要简单地把指令拆开,分发给不同的执行单元。

脉动阵列:数据像心跳一样在阵列中「脉动」传递。每个处理单元只做简单的乘加操作,数据从一端流入,结果从另一端流出。Google的TPU就是靠这个起家的。

VLIW的优缺点:

  • ✅ 硬件简单,面积小
  • ✅ 编译器可以全局优化
  • ❌ 对编译器要求极高(我见过团队花两年调编译器)
  • ❌ 指令宽度固定,灵活性差

脉动阵列的优缺点:

  • ✅ 极高的计算密度(一个芯片能塞几千个乘加器)
  • ✅ 数据复用率高,省带宽
  • ❌ 只能做规整的矩阵运算
  • ❌ 对不规则计算(如稀疏矩阵)效率极低

我个人习惯:对于专用NPU(比如只跑卷积),脉动阵列是王道。但对于通用NPU(要支持各种网络),VLIW加上SIMD的组合更灵活。

举个例子:我在设计一款边缘NPU时,最初选了脉动阵列。结果客户要求支持Depthwise卷积——这玩意儿在脉动阵列上跑起来,利用率不到10%。后来我们改成VLIW+SIMD架构,虽然峰值算力低了点,但实际应用场景的吞吐量反而更高。

我的经验:别迷信峰值算力。一个架构好不好,要看它在真实模型上的利用率。脉动阵列的峰值算力很诱人,但一旦遇到不规则计算,就变成了「纸面英雄」。

3.4 如何选择?一个实战框架

好了,理论讲完了。你可能会问:那我到底该选哪个?

我建议按以下步骤来:

  1. 明确目标场景:是固定网络(如ResNet-50)还是动态网络(如BERT)?
  2. 评估数据规整度:卷积、全连接这类规整操作多,还是稀疏、动态操作多?
  3. 权衡面积和功耗:脉动阵列面积效率高,但灵活性差;VLIW面积大,但适应性强。
  4. 考虑编译器成熟度:VLIW对编译器依赖极高。如果你团队没有编译器专家,慎选。

一个典型组合:

  • RISC风格的指令集(灵活、易扩展)
  • SIMD作为数据并行基础(规整计算高效)
  • VLIW作为指令调度手段(编译器优化空间大)
  • 可选:小规模脉动阵列作为加速器(专门处理矩阵乘法)

嗯,这个组合我在多个项目中验证过,效果不错。当然,没有银弹。每个选择都有代价,关键是你得清楚自己愿意为哪个「优点」买单,又能接受哪个「缺点」。

最后说一句:指令集设计不是纸上谈兵。我建议你拿一个真实网络(比如MobileNet)跑一遍仿真,看看不同架构下的实际吞吐量和功耗。数据会告诉你答案。