做计算机系统设计,几乎不存在“更快、更省电、更便宜”同时成立的完美答案。真正的工作,通常是在功耗、性能、面积三者之间做受约束的最优化;而真正的难点,则是在测量时别把自己骗了。把这两件事放在一起看,才能理解为什么芯片设计从来不是单点冲刺,而是一连串清醒的取舍。
PPA 不是口号,而是设计的基本现实
芯片设计里最常见的三个目标是 PPA:Power、Performance、Area,也就是功耗、性能和面积。它们不是三个可以同时拉满的滑杆,更像是三条互相牵制的约束。通常的做法是先固定其中两个,再尽量优化第三个。现实产品里会有平均功耗预算、峰值功耗预算,还会有面积预算;在这些限制之下,目标往往是把性能推到尽可能高,只要不低于可接受下限就行。
面积之所以重要,不只是因为版图看起来大或小,而是因为它几乎直接映射成本。晶体管越多,制造成本通常越高;芯片越大,良率通常越差;封装成本也会跟着上升。更现实的是,芯片尺寸还会受到光刻 reticle limit 的硬上限约束。哪怕设计再好,只要超出可制造范围,就不是一个可交付的产品。
所以,PPA 的本质不是“同时最大化三件事”,而是先承认目标冲突,再明确优先级。
先分清功耗和能耗,再谈优化
功耗是瓦特,能耗是焦耳。两者密切相关,但不是一回事。能耗等于功耗乘以时间。一个设计的瞬时功耗低,并不自动意味着它完成同样任务时更省能;如果它只是更慢,那么总能耗未必更低。
功耗又可以拆成动态功耗和静态功耗。动态功耗来自晶体管切换,静态功耗主要来自漏电。平均功耗直接影响续航,峰值功耗则关系到最大电流需求、电源稳压设计,以及散热系统的上限。很多时候,散热不是由平均值单独决定的,而是平均值和峰值一起决定的。
动态功耗最常见的近似关系是:
P ∝ αCV²f
这里的 α 是开关活动率,C 是电容,V 是供电电压,f 是频率。这个式子很重要,因为它直接告诉你,降功耗通常只有四条路:减少切换活动、降低频率、减小电容、降低电压。比如用 clock gating 可以压低无效切换;用更小的晶体管或更紧凑的设计可以减小电容;而降低电压往往是最“猛”的那一招,因为它不仅直接降低 V²,还通常会把可达频率一起拉低。把这种联动考虑进去后,功耗常常近似跟 V³ 同阶变化。
这就是为什么降压常常像“暴击”一样有效。
电压并不是越高越好,也不是越低越好
很多人直觉上会觉得:想要性能就加压,想要省电就降压。问题在于,这条曲线不是线性的,收益和代价很不对称。
一个典型例子是把 1.1V 当作 nominal 点来看:升到 1.2V,频率大约只多 20%,但功耗会增加 40%;降到 1.0V,性能损失约 20%,却能省下约 30% 功耗;再降到 0.9V,性能大约少 35%,功耗却能少 40%。这说明过驱动区经常是“多花很多电,只换回一点性能”,而中度降压则可能是“损失有限性能,换回很可观的能效”。
换句话说,工作电压的选择,本质上是在一条弯曲的性能—功耗曲线上找 operating point。你不是在选“快”还是“省”,而是在选“这笔交换到底值不值”。
真正高明的电源管理,发生在系统忙的时候
传统电源管理很擅长处理“没事干”的场景,比如睡眠模式、待机模式、深度休眠。这当然重要,但只解决了一半问题。另一半更关键的问题是:系统在工作时,应该跑多快?
主动模式下的能耗管理,核心思想不是“永远跑最快”,而是“只跑到刚好满足 deadline”。因为跑太快然后闲着,本质上是在浪费电。睡眠态优化和主动态优化并不冲突,它们是正交的:一个负责没活时别乱耗电,一个负责有活时别过度配置性能。
动态电压调节和动态频率调节(DVFS/DVS)就是这类方法的代表。把电压从 1.4V 降到 1.0V,理论上可能带来接近 2 倍量级的能耗收益。更进一步,运行时算法还可以持续监控应用行为,动态选择“对用户无感、但足够低”的性能档位。比如视频播放、界面交互这种任务,只要帧率、画质和人眼可感知的延迟不变,系统就没有必要一直维持最高性能档。
这类思路并不新。早在 2000 年前后,就已经有处理器支持动态电压和频率调节,并借助软件层实现预测式的性能/电压控制。今天看这是常识,当年其实非常超前。
“更省电”的优化,不一定真比降压更好
很多设计讨论之所以会绕晕,是因为大家喜欢拿单一指标下结论。比如常见的 MIPS/W,看起来很直观:同样的瓦数跑更多指令,当然更好。问题是,这个指标会把“做了多少工作”和“为了做这些工作拖慢了多少时间”混在一起,有时会得出误导性的结论。
来看一个简单例子。基线处理器是 80W、1 BIPS、1.5V、1GHz。现在做一个 cache 优化,结果是功耗下降 20%,性能下降 10%,最终变成 64W、900 MIPS。按 MIPS/W 算,能效确实提升了,大约是 1.125 倍。可这还不够说明它就是“更好的处理器”。
如果换个角度,用 PDP、EDP、ED²P 这类同时惩罚时间增长的指标来衡量,结论就没那么漂亮了。PDP 是功耗乘延迟,本质上就是能量;EDP 再乘一次时间,会更重地惩罚慢设计;ED²P 则惩罚得更厉害。那个 cache 优化在 EDP 上只略好一点,到了 ED²P 甚至反而更差。
更有意思的是,如果目标只是把功耗降 20%,那未必需要改 cache。因为在 V 与 f 近似联动的前提下,功耗近似跟 V³ 变化。把电压和频率同时各降大约 7%,就能把功耗压到 0.8 倍左右;这样在同样 64W 下,性能大约还能保住 930 MIPS,反而比 cache 优化的 900 MIPS 更高。
类似地,如果一个 75W 的基线处理器,有人提出一种优化,能把功耗降到 70W,但性能也降 5%,那它听上去似乎不错。可如果只是做 DVFS,在性能同样降低 5% 的情况下,功耗大约可以降到 64.3W。也就是说,在这个场景里,直接降压降频比专门的结构优化更划算。
这不是说微架构优化没价值,而是说:任何“更高能效”的结论,都必须问一句——和简单的电压/频率调整相比,真的更好吗?
并行化也能省能,但账不会白送
另一个很容易被误解的点是并行化。把一个任务拆给两个更慢的单元去跑,往往可以在更低电压、更低频率下完成同样工作,从而显著降低动态能耗。某些理想化分析里,能耗甚至可以接近减半。
但这张账单不会凭空消失。并行化通常意味着面积翻倍,晶体管更多,漏电更高,设计和互连也更复杂。也就是说,它是用面积和静态功耗去换动态能耗。这个交换在移动设备、数据中心、边缘设备上是不是值得,答案经常不同。
所以并行不是免费午餐,它只是另一种取舍方式。
面积不是“最后再看”的指标
很多新手做设计时,会把面积当作一个后验结果:先把性能做上去,功耗再压一压,最后看看面积能不能接受。现实往往恰好相反。面积一旦超标,成本、良率、封装、制造可行性会一起出问题。晶体管更多,不只意味着芯片更贵,还意味着缺陷命中的概率更高,最终可卖出的裸片更少。
这也是为什么“Area is a proxy for cost”几乎是硬件设计里的常识。面积不只是“版图工程师的事”,而是系统级经济模型的一部分。
设计空间里,真正值得做的是 Pareto 前沿上的点
当你同时看多个指标,比如功耗和性能,就会得到一大片设计点。不是每个点都值得实现。真正有价值的,是 Pareto frontier 上的点:在你关心的所有指标上,都没有被别的方案全面压制的设计。
如果某个方案 A 在性能更差的同时功耗还更高,那它就是被支配点,理论上没有实现价值。Pareto 前沿的意义,不是告诉你哪个点“绝对最好”,而是先帮你把明显不该做的方案删掉,然后再让你按产品目标做最终选择。
这也是 PPA 讨论里最成熟的思维方式:先找前沿,再谈取舍。
Benchmarking 的第一步:先说清你测的是延迟还是吞吐
谈完设计,再谈测量。因为如果性能都没测对,前面的优化就没有意义。
“性能”至少有两个完全不同的定义。第一种是延迟,也就是完成一个固定任务要多久;第二种是吞吐,也就是单位时间能完成多少任务。它们经常不是一个方向的目标。吞吐能利用并行,延迟往往不能;为了提高吞吐而做的设计,常常会让单任务延迟变差。
一个很直观的例子是运送乘客。小汽车一次只能带 5 个人,但速度 60 英里每小时;大巴一次能带 60 个人,但速度只有 20 英里每小时。走 10 英里,小汽车的延迟是 10 分钟,大巴是 30 分钟,所以小汽车在延迟上快 3 倍;但按人/小时算吞吐,小汽车只有 15 人/小时,大巴却有 60 人/小时,所以大巴在吞吐上高 4 倍。
这说明一句话:不先定义性能,任何“更快”都可能是错的。
速率不能乱平均,MIPS 也不能乱比较
性能数据最容易出错的地方,是把“时间”和“速率”混为一谈。时间可以直接相加,速率不行。比如 1 英里用 30mph 开,再 1 英里用 90mph 开,平均速度不是 60mph,而是总路程除以总时间,结果大约只有 47mph。原因很简单:速率必须先转回时间,再做聚合。
同样的错误也经常出现在处理器比较里。MIPS 是每秒执行多少百万条指令。只有在 ISA、编译器和动态指令数都基本固定时,它才勉强有比较意义。一旦编译器优化改变了程序的动态指令数,较低的 MIPS 完全可能对应更快的完成时间。频率也是一样,跨微架构比较时尤其不可靠,因为更高主频并不自动意味着更低 CPI,也不意味着更短的真实运行时间。
真正稳妥的出发点,仍然是性能铁律:程序执行时间由动态指令数、每条指令平均周期数和时钟周期共同决定。只盯着其中一个数字,几乎一定会看偏。
平均值怎么取,不是数学洁癖,而是结果是否靠谱
基准测试一多,就绕不过“怎么求平均”。这里最容易犯的错误,是图省事。
如果你平均的是时间,应该优先用算术平均。 如果你平均的是速率,应该优先用调和平均。 如果你平均的是无量纲比值,比如 speedup ratio,几何平均才比较合适。
这背后的原则并不复杂:你最后希望这个单一数字,能真实对应“完成这一组工作实际要花多少时间”。如果指标本身是时间,就用和时间一致的聚合方式;如果指标本身是速率,就要用和总工作量除总时间一致的方式。
问题在于,工业界很喜欢把很多 benchmark 压缩成一个漂亮的单一分数,而这个分数未必真的忠实于总时间消耗。于是“一个数字看天下”常常只是方便发布,不一定方便判断。
Dhrystone、SPEC 和单一分数的诱惑
早年的 Dhrystone 就是一个典型例子。它的目标是“代表性强、容易运行”,测试体量很小,定时区间里动态执行大约 100 条语句,因此 DMIPS 曾长期被一些人拿来当嵌入式处理器性能代理。但这种做法非常容易失真。程序太小,容易全部装进 cache;指令混合可能已经过时;源码可控性差;容易被编译器技巧“刷分”;运行时间太短还会影响复现精度。
后来的 SPEC 基准就是为了解决这些问题。它把通用计算负载做得更系统:SPECCPU 更偏单核任务的速度/延迟,SPECRate 更偏多处理器系统的吞吐。它比 Dhrystone 一类测试成熟得多,也更难被单点投机。
但即便如此,问题也没有彻底消失。SPEC 之所以采用几何平均,是因为它方便发布、给每个子测试相同权重、面对无量纲比值时看起来合理,而且不容易被少数长时间运行的程序“带偏”。这些理由都成立,但反对意见同样强:如果你最终关心的其实是完成整组工作所花的总时间,那么无论是时间型指标还是速率型指标,都应该优先选择能真实反映总时间的聚合方式,而不是先把每个 benchmark 各自归一化,再求一个看起来体面的平均数。
说得更直白一点:单一综合分数很方便,但方便不等于真实。
真正的性能提升,常常是“这个负载下、这个线程数下、这个功耗下”的提升
现代处理器宣传里,benchmark 图表非常常见,但读这些图时一定要带着机制去看,而不是只看柱状高度。
同一代处理器在不同工作负载上的收益,可能差得很大。通用整数测试上也许只有十几个百分点;Memcached 这样的缓存服务,提升可能在 13% 到 15% 左右,而且随着更新比例上升,系统会越来越受其他瓶颈限制;NGINX 这类 Web/代理服务,可能有 20% 到 32% 的提升,而且线程数越高越明显;数据库负载如 Percona MySQL,收益甚至可能到 35% 到 104%,背后常常是分支预测、取指和硬件预取等机制的改进;到了 XGBoost 一类机器学习任务,收益又可能跳到 67% 到 114%。
这正说明了 benchmark 最重要的一件事:它测到的不是抽象的“性能本身”,而是某个系统在某类工作负载上的表现。甚至“同功耗下吞吐翻倍”这样的说法,也必须追问:在哪些工作负载上?是单核还是整机?内存系统怎么配?线程数怎么选?瓶颈在核内、在缓存、在内存、还是在系统软件?
脱离上下文的 benchmark 结论,几乎都不可靠。
最后,别再假装可以同时最大化一切
无论是 PPA,还是 benchmarking,最后都指向同一个事实:你不能在没有优先级的前提下,同时最大化多个目标。既想要最高性能、最低功耗、最小面积,又想要一个数字完整总结系统好坏,这两件事本质上都在回避冲突。
成熟的做法不是否认冲突,而是把冲突说清楚:
你关心的是延迟还是吞吐? 你受限的是平均功耗、峰值功耗,还是面积成本? 你要优化的是单任务体验,还是整机吞吐? 你需要一个便于宣传的数字,还是一个真正能指导设计决策的模型?
一旦这些问题被说清楚,很多争论会自己消失。因为芯片设计从来不是寻找唯一正确答案,而是在约束明确之后,选择那个最符合目标的答案。真正专业的系统设计,不是追求“全都要”,而是知道什么时候该要什么,什么时候该放弃什么。