本节摘要:性能优化最怕两件事——没有数据就动手、优化目标互相打架。本节建立三本账的纪律:内存账(栈深堆深水位与池化)、开销账(切换税、滴答税、临界区税的测量与减免)、实时账(延迟预算与余量管理),每本账都有"先测量、再决策、后验证"的闭环。随后给出反模式清单——那些看起来在优化、实际在制造新问题的操作。优化不是玄学,是有账本的资源调配。
资源受限的世界里,优化的本质是调配:把处理器时间与内存从浪费处挪到需求处。调配的前提是知道现状——所以本节每一条建议的前置都是"先量",而 7.2 节的统计面板与 6.4 节的时间预算表正是两份账本底稿。
嵌入式优化的第一战场几乎总是内存——代码体积还能靠裁剪,随机存储器超了就是超了。三步流程。
第一步,盘点。用任务快照拿全栈水位(历史最深用量,2.4 节)、堆画像(当前与历史最低空闲,5.2 节)、静态区的链接报告。第二步,收紧。栈深收紧到"历史峰值加四分之一余量"——水位测量覆盖了已走过的路径,没走到的(最深错误处理分支)靠余量兜底;宁可留足,不追求贴线。堆按"历史最低空闲加两成"定尺寸;动态对象池化(5.1 节的方案一复兴)把最大的一块不确定变成确定。第三步,结构性节省。优先级最高的手段是砍大局部缓冲(2.4 节的教训反向使用:把大数组移出栈,静态区一个数组比十个任务栈里各留一份省得多);其次是任务合并——两个通信节奏相近的任务合成一个状态机,省一整份栈加控制块(6.1 节定时器化是它的温和版);最后才是参数微调类(任务名长度、统计缓冲尺寸)。
| 手段 | 收益量级 | 风险 | 前置条件 |
|---|---|---|---|
| 大缓冲出栈 | 高 | 低 | 审计重入性 |
| 对象池化 | 高 | 中(池满策略要设计) | 并发模式固定 |
| 任务合并或定时器化 | 中 | 中(职责耦合) | 节奏相近 |
| 栈深收紧 | 中 | 中(水位未覆盖分支) | 水位数据充分 |
| 关闭未用功能宏 | 中 | 低 | 配置审计 |
| 参数微调 | 低 | 低 | 无 |
第 3 章算过价目表,这里讲收税与减税。切换税:每次切换数百周期,税负等于切换频率乘单价。减免三招:事件驱动化(2.2 节按键改造是模板——轮询改等待,切换次数断崖式下降);合并高频同类任务(两个毫秒级任务合成一个,减一半的唤醒切换);收敛优先级差(同级轮转的切换比跨级抢占便宜,但别为此牺牲实时性——这条只在"同级人为拆细"的场景适用)。滴答税:频率乘固定成本。减免靠降频(一千赫兹降到一百赫兹,税负砍九成,代价是延时粒度变粗——对延时精度没要求的大多数业务毫无影响)与无滴查模式(空闲时段整段免税,6.2 节)。临界区税:关中断期间所有被屏蔽中断的延迟。减免只有一个方向——缩短(3.3 节的纪律),把区内的计算外移、把长操作降级到调度器挂起或改无锁结构。
三种税的减免优先级按"测量暴露的最大项"定:6.4 节的三组测量直接给出答案。经验上,多数系统的最大项是长临界区与长中断(不是内核切换)——先量再动,避免对着小税项猛优化。
实时优化的目标不是"更快",是"最紧的那条截止还有多少余量"。账本就是 6.4 节的时间预算表:每个关键事件的最大允许延迟、实测最大值、余量三列。优化次序按余量升序——余量最小的链路先动,手段按 8.1 的五区定位(时序区的反转、中断区的长中断、内存区的换页式卡顿都可能是余量杀手)。
实时账有一个反直觉的原则要写进团队规范:余量是安全资产,不是浪费。把所有链路都优化到余量两三毫秒的"高效"状态,意味着任何一次需求变更或负载波动都会击穿截止——脆弱的高效不如从容的达标。健康状态是:关键链路余量稳定、可解释、有记录;非关键链路不占优化资源。
反模式一,盲降滴答频率。不查业务对延时粒度的依赖就砍到十赫兹,某处十毫秒的超时逻辑变成了一百毫秒——省了百分之一处理器,赔了十倍延迟。反模式二,栈砍到贴线。水位刚好够就砍到水位加几字,没走的错误分支一来就是 2.4 节的工单重演。反模式三,优先级调高解决一切。某任务慢就升它的优先级,半年后优先级表报废(2.3 节的蠕变),切换税还升了。反模式四,过度池化。给三种只用一次的对象也建池,代码复杂度换不来收益——池只给"高频领还"的对象。反模式五,关抢占求稳定。协作模式掩盖了真实竞争,实测"稳定"了,实时性已死(2.3 节配置表)。反模式六,无测量回归。优化完不复测三本账,收益全凭感觉——下次重构时连"当时为什么这么配"都答不上。
⚠️ 优化的最大风险是隐性回归:内存账的收紧可能在极端分支爆栈,开销账的合并可能改变响应顺序。任何优化提交必须附带两样:优化前后的三本账对比数据、覆盖极端路径的回归测试结果。没有数据的优化提交,代码评审应当直接打回。
背景。某控制器新需求要加网络功能,内存缺口百分之十五,处理器余量尚可。三本账盘点:栈水位表显示三个任务余量超过八成(历史遗留的"翻倍配置"),堆的历史最低空闲不到一成;切换税测量显示健康,滴答税正常。决策:缺口在内存账,按收益排序动手——三个任务栈收紧(回收约一成);网络任务的大缓冲静态化并移出栈;日志缓冲池化(高频领还,池化收益确定);未用的队列集功能宏关闭。结果:内存缺口补平且留出余量,处理器侧零改动。回归测试覆盖最长错误处理路径,水位复测全线健康。解读:这次冲刺没改一行"更快"的代码——账本指明缺口在内存,内存的浪费在配置,配置的浪费在"没量过"。变式:若缺口在处理器(实时账余量告急),同样的盘点会把火力导向长临界区与事件驱动化——账本思维的价值正在于每次都把力气花在刀刃上。
内核侧的功夫到本节全部讲完。下一节向外看:把系统接入网络与云生态——协议栈怎么接、内存怎么分、安全怎么设、断网怎么活。