最近不少朋友在搞大模型部署和AI算力选型的时候,都碰到过一个挺烧脑的现象:一块AI芯片,官方标称的TOPS、TFLOPS都很漂亮,可真跑起推理来,token生成速度总是对不上账。一开始大家习惯性查显存带宽、查算子实现、查推理框架的配置,兜兜转转排查好几轮,最后才发现卡脖子的地方,往往不是执行单元不够多,而是指令发射单元不够强。
指令发射单元(Instruction Issue Unit)这个名字听着很硬件,但它的角色可以用一个生活场景来理解:它是处理器内部的“十字路口”。取指阶段负责读出指令,解码阶段负责把指令翻译成内部微操作,执行阶段负责真正干活,而指令发射单元就站在中间,决定“哪条指令现在可以放行、放行到哪个执行端口”。这个十字路口每个周期能放行多少指令、遇到冲突时能不能聪明地重排,直接决定了下游一大排执行单元能不能吃饱。执行单元吃不饱,算力天花板自然就低。这个道理,放在CPU、GPU、NPU上都成立,只是表现形态不同。
这篇文章想把这个“十字路口”讲透:它到底是什么、怎么影响AI芯片的算力天花板、为什么同样是AI芯片有的用复杂发射逻辑、有的却刻意做得极简,以及在评估算力和做实际部署时,怎么从指令发射的角度找到真正的瓶颈。不管你是做芯片设计的、做算法优化的,还是日常要选显卡、租算力跑大模型的,这篇内容应该都能让你对“算力”这两个字有不一样的理解。
1. 指令发射单元到底是什么——算力数字背后的“隐形运营中心”
1.1 一条指令从“取指”到“执行”要经过几个路口
要理解指令发射单元,最好先过一遍指令执行的完整流水线。以一条典型的向量计算指令为例,它在处理器内部的旅程大致是:
- 取指(Fetch):从指令缓存中读出指令。注意这里取的是“指令”,不是数据。
- 解码(Decode):把指令翻译成处理器能直接操作的微操作。一条复杂指令可能拆成好几条微操作。
- 寄存器重命名(Rename):解决寄存器名字冲突,把逻辑寄存器映射到物理寄存器,为乱序执行创造条件。
- 派遣(Dispatch):把微操作送进发射队列,等待操作数就绪。
- 发射(Issue):发射单元扫描发射队列,找出“源操作数都已就绪且对应执行单元空闲”的指令,把它们送往执行端口。这一步就是指令发射单元的核心工作。
- 执行(Execute):执行单元真正做算术。
- 写回(Write-back):把结果写回寄存器文件,或者通过旁路转发给后续指令。
第五步“发射”就是本文的主角。它的复杂之处在于,执行单元有多个,比如整数ALU、浮点单元、向量单元、访存单元,每个执行单元同一周期只能接受一条指令,所以“谁先用这个端口”必须有个仲裁者。指令之间还有依赖关系——后一条指令可能要用前一条指令的结果,在前一条算完之前,后一条即使拿着端口也不能执行。发射单元正是在“端口有限、依赖交错”的局面下,尽量在每个周期里挑出可以并行执行的指令,送进执行单元。
从数据流的角度看,发射单元就是指令从“前端”流向“后端”的必经之路。前端(取指-解码)把指令流水化地喂进来,后端(执行单元)吞吐量再大,也要靠发射单元一点一点地转发。转发速度跟不上,后端就空转。所以说它是“十字路口”,一点不夸张。
1.2 发射单元如何直接影响“峰值算力”的兑现率
“算力”这个词最近几年被厂商宣传得很热闹,但脱去包装,算力公式其实可以拆成两大类。先看理论峰值:
理论峰值算力 = 核心数 × 每个核心每周期可执行的运算次数 × 时钟频率
以GPU为例,理论FP16算力就是SM数量乘上每个SM每周期的FP16操作数再乘Boost频率。这块数据厂商都会标,也就是大家常看的“显卡AI算力TOPS排行”“GPU算力TOPS对照表”里那个数字。但真正决定业务体验的,是另一个式子:
有效算力 = 理论峰值算力 × 发射效率 × 执行单元平均利用率
发射效率可以理解为“每周期实际发射的指令数(IPC)”和“理论最大发射宽度”的比值。如果指令发射单元设计成每周期最多发射4条指令,但实际运行中平均只有1.5条指令能成功发射,那发射效率就是37.5%。这37.5%会直接传递到执行单元的利用率上,最终表现为“理论算力打了几折”。
我见过不少AI加速卡跑通用模型时,实际有效算力只有理论值的五成上下,甚至更低。很多团队一开始把锅甩给内存带宽,其实是忽略了指令发射层面的浪费:发射队列长期空转、指令等待源操作数、发射端口发生结构性冲突。这些浪费在纯GEMM Benchmark里不明显,因为矩阵乘法的循环结构规整、依赖简单,编译器能提前把指令调顺,发射队列被喂得很满。但真实部署的时候,模型里混合着卷积、注意力、归一化、动态shape、控制流,指令流变得复杂之后,发射单元的调度压力立刻暴露出来。
这也是很多人困惑“为什么跑Benchmark跑不过别人,真上线又被反过来吊打”的核心原因之一。
2. 从“一个时钟周期”谈算力上限:发射宽度、指令窗口与乱序发射的取舍
2.1 发射宽度不是越大越好
发射宽度,通俗讲就是这个“十字路口”每个时钟周期能同时放行多少辆车。单发射、双发射、四发射、六发射是常见配置。现代高性能CPU面向单线程性能,发射宽度一般做到6到8,配合很深的重排序缓冲;GPU则是把32个线程打包成一条warp指令,一个调度器每周期发射一条warp指令,等效于“单周期32路数据并行发射”;而许多NPU干脆只有一个极简的发射口,一次发射一条“整算子指令”,后续数据流交给硬状态机。
宽发射的好处很直观,理论上每个周期能让更多指令进入执行阶段。但代价也很大:
- 执行端口必须足够多,否则发射出去没人接;
- 仲裁逻辑高度复杂,发射宽度每加一,端口冲突的仲裁难度和硬件面积几乎呈二次方增长;
- 需要匹配足够大的发射队列和重排序窗口,才能保证每个周期有足够多“就绪”指令可发。
如果把执行单元比作高速公路车道,那发射单元就是收费站窗口。车道修得再多,收费站窗口不够,车流照样排队。反过来窗口太多,收费站本身占地太大,整条路的成本结构就失衡。所以高端的CPU普遍选择“深窗口、中宽度”,而不是“浅窗口、大宽度”——窗口深,才能容纳足够多的“等待者”,让重排有更多选择空间。
2.2 乱序发射:重排指令队列带来的隐藏延迟收益
乱序执行的核心,是把“按程序顺序发射”的限制尽量拿掉,只保留真正的数据依赖关系。发射单元每周期扫描发射队列,挑出所有源操作数都已就绪的指令,按照优先级把它们发到相应执行端口。这样的好处是:当一条长延时的访存指令或矩阵乘指令处于等待时,其他独立的标量运算、向量运算可以穿插在这一段时间里执行,把宝贵的时间片填满。
这里要提一个叫“隐藏延迟”的概念。访存指令从发出到数据回来,常常要几百个周期。矩阵乘指令的延迟也有几十个周期。如果在发射时不做乱序重排,后面的指令就只能干等,执行单元的利用率会非常难看。乱序发射相当于让处理器在等待期间去干别的活,比如做LayerNorm、做残差相加、做位置编码。这些操作彼此独立,完全可以在GEMM等待过程中穿插发射。编译器调度配合硬件乱序窗口,能把AI推理里的“空闲气泡”压到很低。
但乱序窗口也不是无限增大的。发射队列里每个周期都要比较所有条目是否就绪、比较所有端口是否可用,这是一组大规模并行比较器。窗口越大、宽度越宽,这个比较器的面积、功耗、延迟就越失控。芯片设计冲刺频率时,往往就得在窗口大小上做妥协。所以在真实芯片里,发射窗口的深度决定了它能“看到”多远未来的指令,也决定了它能重排多大范围的指令顺序。设计者必须在面积与指令级并行之间找平衡。
2.3 为什么AI芯片往往选择“少而精”的发射策略
讲到这里有一个看似矛盾的点:既然发射宽度和乱序能力这么重要,为什么很多AI芯片,尤其NPU,反而把发射单元做得格外简单?
原因在于AI主计算负载的模式非常固定。以卷积或矩阵乘为例,骨干计算就是一个高度规则的嵌套循环,MAC单元要做的只是“乘累加”,操作数从片上缓存连续读入,结果连续写回。这样的模式根本不需要复杂交通疏导——所有车辆走同一条专用快速路,路况恒定。NPU干脆把“发射”从每周期一次简化成“每层算子一次”:一条指令告诉数据流引擎“现在执行这整层矩阵乘”,之后几千上万个周期里,MAC阵列完全由数据通路自己驱动,不再逐条发射指令。省下来的发射硬件面积和功耗,全部塞给MAC阵列,峰值算力自然飙升。
这种“少而精”的发射策略,正是AI芯片和通用CPU在“十字路口”设计上的根本分歧。CPU和GPU面对的是“混合车流”,需要高智能的红绿灯来调度;NPU面对的是“专线车队”,没有必要修复杂的立交桥。但硬币的另一面是:一旦负载里出现动态shape、稀疏性、多分支控制流,或者需要频繁切换的任务,NPU极简的发射单元就会成为新的短板。所以业内常说,AI芯片的峰值算力靠堆硬件,但有效算力靠的是“发射模式”和“工作负载”的匹配度。
3. 同一套原理,三种流派:CPU、GPU、NPU 发射单元设计对比
3.1 CPU:宽发射加深乱序,为“不确定性”买单
CPU面对的负载是全局未知的——操作系统调度、数据库查询、浏览器脚本、科学计算,指令流的形态千奇百怪。为了让这种“不定形”的负载也能榨出性能,CPU选择了非常激进的指令并行策略:4到8路发射宽度、可容纳数百条指令的重排序缓冲、复杂的分支预测和寄存器重命名。代价是芯片面积里很大一部分被发射逻辑和控制逻辑占据,真正干活的ALU占比并不高。
在AI训练场景里,CPU虽然不是主力计算设备,但要做大量的数据预处理、调度、IO,以及小规模的控制流计算。这时候CPU的宽发射和乱序能力反而派上用场——因为这类负载就是“什么指令都有”,需要的就是应变能力。拿算子融合来说,很多推理引擎会先在CPU上做图优化和算子融合,再把计算图下发到GPU或NPU,这部分工作本身就依赖CPU较强的控制流和前端的处理能力。
3.2 GPU:warp级宽发射,把“单条指令”放大成“32路数据”
GPU的设计哲学是“大力出奇迹”:用大量小核心并行处理同构任务。在发射上,GPU用了一个很有意思的技巧——一条指令对应32个线程,也就是一个warp。调度器每周期发射一条warp指令,等效于同时对32个数据通道进行发射。一条FMA(融合乘加)指令在CPU上只能驱动一组乘加器,在GPU上却能同时驱动32组。这让GPU在同样周期内能完成的理论运算次数远超CPU。
GPU的发射单元也做乱序调度,不过粒度是warp级别:当某个warp因为访存等待而无法继续执行时,调度器马上切换到另一个已经就绪的warp,用“线程级并行”来隐藏延迟,而不是像CPU那样依赖“指令级并行”来填满发射窗口。这套机制在AI训练里非常对味,因为深度学习的大量计算可以拆成海量小任务并行执行。这也是为什么大规模训练几乎都被GPU垄断——它的发射模式天然适合海量同构小任务。
不过GPU也有自己的短板。warp调度器每周期只能发射有限条warp指令,如果某个warp里出现分支发散,部分线程执行,部分线程等待,有效发射效率就会下降。当模型代码里充满动态控制流时,GPU的发射效率同样会打折扣,只是没有NPU那么敏感而已。
3.3 NPU:极简发射加数据流驱动,把面积留给MAC阵列
NPU走的是另一条路。以Google TPU为代表的脉动阵列,以及各类DSA设计的AI加速卡,在指令发射阶段都极其克制。它们的典型形态是:一个标量控制器解析指令,把运算参数下发给脉动阵列或MAC阵列,然后大量数据在片上按照既定路径“流”过计算单元,几乎不需要逐周期发射。
这种设计的优势在于计算密度高——在同样面积的裸片上,可以把更多资源投给乘法器和累加器,而不是投给仲裁和调度逻辑。对于以矩阵乘为主体的Transformer训练或推理,这种结构的发射效率和功耗比是非常优秀的。我实测过一些国产NPU,在跑纯GEMM和MLP层时,能效比可以做到比同代GPU高不少。但一旦负载里出现频繁的if-else、动态padding、多batch异长请求,极简发射单元就开始吃力,因为每条分支都要靠标量控制器逐条处理,计算阵列反而闲下来。
3.4 三种架构的发射单元对比
这里整理一个直观的对比表,方便大家做选型参考:
| 维度 | CPU(如Xeon/EPYC) | GPU(如A100/H100) | NPU(如TPU/昇腾) |
|---|---|---|---|
| 发射粒度 | 单线程指令 | warp指令(32线程) | 算子级指令 |
| 典型发射宽度 | 4到8 | 每SM 4个调度器,各发射1条warp/周期 | 1,但一次驱动整个阵列 |
| 乱序能力 | 深重排序缓冲 | warp级切换 | 几乎不用乱序 |
| 控制流处理 | 强 | 中 | 弱 |
| 规则矩阵负载效率 | 低 | 高 | 极高 |
| 不规则负载效率 | 高 | 中 | 低 |
这张表的价值在于提醒:没有哪一套发射单元设计是“绝对最优”的,只有“和负载匹配”的差别。买AI加速卡的时候,不能只看TOPS峰值,更要看自己的业务负载和芯片发射模式的匹配度。拿推理服务来说,如果线上请求基本都是定长的、同构的批量矩阵运算,NPU的“简化十字路口”能省下大量功耗;如果请求长度参差不齐、频繁动态shape,反而是GPU或CPU更能兜住底线。
4. 指令发射单元成为瓶颈时的实战排查方法
4.1 典型瓶颈表现:算得动但跑不快
指令发射单元成为瓶颈时,系统层面会出现一些不那么“直觉”的症状:
- 理论利用率很好看,GEMM测试和TensorCore测试都漂亮,但真实推理延迟就是下不来;
- 频繁切换batch或上下文时,吞吐骤降;单batch性能尚可,多batch性能崩塌;
- 把峰值算力更高的卡换上去之后,性能几乎没有提升——说明瓶颈不在“能算多快”,而在“能喂多快”;
- profile数据里出现大量pipeline stall、issuing queue empty、scoreboard stall等等待事件。
这些症状背后往往指向同一个事实:发射端无法持续向执行单元供指令。要么是前端取指来不及,要么是发射队列里依赖没解开,要么是端口冲突太严重。定位的第一步,就是先区分“前端喂不进来”还是“后端接不住”,两者的优化方向完全不同。
4.2 如何测量发射效率:从perf到硬件计数器
在x86平台上,可以用perf拿到instructions per cycle、前端stall周期、后端stall周期等数据。一条指令从取指到发射的任意环节卡住,都会反映在这些计数器里。单独看IPC可能不够直观,我习惯把“发射了N条指令”和“消耗了M个周期”放在一起看平均每个周期的发射条数,再和芯片标称的最大发射宽度比对,就能算出发射效率。
在GPU上,推荐用厂商的profiler。NVIDIA的Nsight Compute会直接给出warp stall reasons,里面分为“wait”“no instruction”“mio throttle”等,看到“no instruction”占比偏高,基本就是发射队列饥饿;AMD的ROCm也有类似的指令送出统计。对于自研芯片或NPU,硬件设计时预留的performance counter里,最值得关注的两个指标是:
- issued instructions per cycle(IPC),也就是每周期实际发射的指令数;
- stall cycles due to full IQ(发射队列满)和empty IQ(发射队列空)。
发射队列满说明执行端堵塞,发射队列空说明前端喂不进来,各自优化方向完全不同。前者要看执行单元的负载均衡和访存冲突,后者要看取指带宽、指令缓存命中率、解码宽度是否够用。
4.3 软件优化:编译器调度、循环展开、指令缓存
在硬件无法改动的前提下,软件侧能做的事情其实不少,我按性价比排个序:
- 循环展开(loop unrolling):把循环体展开多份,减少循环控制指令占比,提高发射队列的“有效指令密度”。矩阵乘和卷积的gemm kernel里,编译器一般会自动做,但自己手写kernel时值得检查展开因子是不是和硬件发射宽度匹配。
- 指令调度(instruction scheduling):让编译器按依赖链远近重排指令,把长延时指令提前发射,短延时指令填充等待。GCC和LLVM都有调度选项,但跨语言绑定和JIT场景里往往被忽略。
- 内存对齐和预取:避免访存指令在发射后长期阻塞在缓存未命中上。对齐访问能减少跨页、跨cache line带来的额外处理,间接降低发射等待。
- 分支消除:把条件分支替换为算术运算或select指令,减少发射单元遇到分支时的预测失败开销。对NPU这类控制流能力弱的芯片尤其有效。
- 算子融合:把多个细粒度算子融合成一个大算子,减少指令流中频繁的上下文切换和依赖断链。这在TVM、XLA这类编译栈里是标配优化,也能大幅缓解发射压力。
4.4 硬件侧改进思路:多发射、指令融合、依赖预测
如果问题出在芯片设计阶段,那么提升指令发射单元吞吐的思路主要有几条:
- 提高发射宽度,但要同步增加端口和执行单元,否则结构冒险会吃掉收益;
- 指令融合:把两条相关指令合并成一条发射,比如“compare+ brach”融合成一条,CPU和GPU都在用这个手段降低发射压力;
- 提前唤醒和依赖预测:更精细的操作数比较逻辑,让发射单元能更早判断“哪条指令即将就绪”,减少就绪判断延迟;
- 分布式发射:把发射逻辑拆成多个子队列,各管一组执行单元,降低中央仲裁的压力。
这些方案没有一个能免费获得,最终都是在面积、功耗、频率、复杂度之间做工程取舍。芯片设计团队如果只追求峰值TOPS,忽略发射端在各式真实负载下的吞吐能力,那做出来的芯片很容易Benchmark好看、落地拉胯。
5. 从指令发射效率看懂 AI 芯片真实算力——选型与调优实操
5.1 重新理解“算力”:理论算力、有效算力与换算模型
后台经常被问到“到底什么是算力”。算力在AI语境里,通常指芯片单位时间内能完成的运算次数,单位是TOPS(整数)或TFLOPS(浮点)。但看一个芯片能否真正满足业务,我更推荐看“有效算力”:
有效算力 = 发射效率 × 执行单元利用率 × 频率 × 运算宽度
这个公式把理论峰值里的“虚胖”部分剥掉了。同一块芯片,在不同框架、不同模型、不同batch size下,发射效率和利用率都会变化,所以有效算力不是一个固定值,而是一个范围。这也是为什么很多人发现“算力、token、API”这几个词经常被混着说——其实它们压根不是一个维度:算力是硬件资源,token是文本建模单位,API是服务接口,三者之间有换算关系,但不能直接划等号。
5.2 用token生成延迟评估指令发射效率
模型推理场景中的token生成速率,是一个更贴近业务的“算力试金石”。token生成本质上是串行的逐token解码,每一步都有大量访存和矩阵运算。一个芯片如果发射单元调度能力弱,在长序列推理时就会出现明显的访存等待暴露,token生成延迟飙升。反过来,如果两块芯片的峰值算力接近,但实测token生成速度差20%以上,大概率就是发射效率和内存层次设计的差距。
评估token算力需求时,可以用一个粗估公式:
每token计算量 ≈ 模型参数总量 × 2
以7B模型fp16为例,一次前向推理的乘加计算量大约是14 GFLOPs,如果目标生成延迟是20ms,那至少需要700 GFLOPS的持续吞吐。但这个数字只是“算术工作量”,真实的芯片还得考虑发射效率、内存带宽、并行度,实际算力需求往往是这个数字的两倍甚至三倍。所以选型时我建议做两个测试:一个GEMM密集测试,一个真实Transformer推理测试,两者结果交叉看,才能识别出哪些芯片的“算力”只在规则计算里好看,放到真实模型里就暴露发射瓶颈。
5.3 选型与部署的几条实操建议
- 不要只看GPU算力TOPS排行和TFLOPS对照表,那只是“峰值”维度。优先参考厂商官方提供的真实模型推理benchmark,比如MLPerf、vLLM的实测token速度,并且用自己的模型跑一遍小规模测试再批量采购。
- 如果是租算力平台,一定要问清楚机型、驱动版本、推理框架版本。同一个型号的卡,在不同软件栈下发射效率能差出30%,这个差距很多时候比换卡更值得先处理。
- 多卡服务器场景,注意指令发射单元的表现会受通信影响。all-reduce等待、数据加载不均衡都会造成发射队列饥饿,训练框架里的数据并行和流水并行配置要一并调优。
- 管理多台算力服务器时,命令层面关注功耗、温度和利用率三个维度,用nvidia-smi或类似工具持续采集。当GPU利用率高但MFU(模型算力利用率)低时,优先怀疑调度与发射层面的浪费,而不是马上换更大算力的卡。
- 给算力池做容量规划时,不要按理论峰值加总。每台机器至少要预留20%到30%的发射冗余,否则几个推理实例一起跑,发射队列一争抢,整体吞吐反而下降。
最后分享一个我自己比较喜欢用的诊断小技巧:在真实推理服务里,把batch size从1逐渐调到4、8、16,同时记录token生成速度。如果吞吐几乎线性增长,说明发射和调度层面还有余量;如果吞吐从某个batch size开始突然平掉,往往意味着发射队列或计算单元已经喂满。配合这个技巧,再回看发射效率相关的计数器,基本就能定位到一块AI芯片真正的算力天花板在哪里。