第4章:关键路径识别——从App发起Capture到HAL返回Result的完整链路
做Camera HAL性能优化,第一件事不是调参数,而是搞清楚——时间到底花在哪了。
我见过太多团队,上来就怼HAL层的ISP tuning,结果折腾两周发现瓶颈在App侧的buffer分配。嗯,这种事我也干过。所以这一章,咱们把整条链路拆开来看,从App发起Capture到HAL返回Result,每一段都扒清楚。
4.1 完整链路全景图
先给你一个宏观视角。一次Capture请求,大致走这么几步:
- App发起请求 → CameraService接收
- CameraService解析 → 下发到HAL
- HAL处理 → 驱动Sensor曝光 → ISP处理 → 生成Result
- Result返回 → CameraService → App回调
你想想看,这中间每一跳都有开销。我习惯把整条链路分成三段:App侧、Framework侧、HAL侧。瓶颈往往藏在交界处。
核心观点: 90%的性能问题,在链路图上就能看出端倪。别急着优化,先画图。
4.2 各阶段耗时分布
我在项目中做过一次完整的trace分析,拿到的数据大概是这样的(以30fps预览为例):
| 阶段 | 典型耗时 | 占比 | 说明 |
|---|---|---|---|
| App → CameraService | 2-5ms | 5% | Binder通信 + buffer queue |
| CameraService → HAL | 1-3ms | 3% | HIDL/AIDL调用 |
| HAL内部处理 | 15-25ms | 60% | Sensor + ISP + 3A |
| HAL → CameraService | 1-3ms | 3% | Result回传 |
| CameraService → App | 2-5ms | 5% | 回调 + buffer传递 |
| Buffer管理/等待 | 5-10ms | 24% | dequeue/queue buffer |
看到了吗?HAL内部占了60%,但Buffer管理那24%才是最容易踩坑的地方。我有个血的教训——曾经优化HAL内部优化到极致,结果发现App侧buffer一直没回来,白白等了8ms。
4.3 如何定位瓶颈——我的三板斧
4.3.1 第一板斧:抓trace
说白了,没有trace数据,你就是在盲调。我个人习惯用这么几个工具:
- systrace / Perfetto:看Framework到HAL的调用时序
- atrace:加自定义标签,把HAL内部的关键节点打出来
- kernel trace:看驱动层和中断响应
举个例子,我在HAL的processCaptureRequest入口和出口各加一个trace点:
// HAL层关键路径打点
ATRACE_BEGIN("HAL_processCaptureRequest");
// ... 实际处理逻辑 ...
ATRACE_END();
// 在ISP处理前后也打点
ATRACE_BEGIN("ISP_3A_compute");
// 3A计算
ATRACE_END();
ATRACE_BEGIN("ISP_raw_process");
// raw域处理
ATRACE_END();
这样抓出来的trace,一眼就能看出哪一段在拖后腿。我记得有一次发现ISP_3A_compute占了18ms,后来发现是AE收敛算法里有个冗余的histogram计算,砍掉后直接降到6ms。
小技巧: trace点别打太多,否则trace文件太大,分析起来反而慢。我一般只打5-8个关键节点。
4.3.2 第二板斧:看buffer流转
很多性能问题,根源不在计算,而在buffer等太久。你需要关注:
- App侧的dequeueBuffer耗时
- HAL侧的requestBuffer耗时
- SurfaceFlinger的queueBuffer耗时
我曾经遇到一个case:预览帧率死活上不去,抓trace发现每次HAL处理完,等App的buffer等了6ms。后来查出来是App用了CPU读buffer,导致GPU和CPU之间同步开销巨大。解决方案?让App改用GPU直接渲染,问题解决。
4.3.3 第三板斧:逐段压测
当你怀疑某一段有问题,但又拿不准时,我建议做分段压测:
- 先mock掉HAL,只测App→Framework的吞吐
- 再mock掉Sensor,只测HAL内部的处理能力
- 最后全链路压测,看哪一段先撑不住
这个方法帮我定位过至少5个隐蔽瓶颈。比如有一次,全链路只能跑到22fps,分段压测发现HAL内部能跑到45fps,App侧能跑到35fps,但合在一起就掉到22fps。最后发现是CameraService的binder线程池不够用,请求排队了。
避坑指南: 我曾经犯过一个错——只测了单帧延迟,没测连续帧的稳定性。结果上线后用户反馈偶尔卡顿,查了三天才发现是某个ISP模块在连续跑20帧后会触发thermal throttling。所以,压测一定要跑够时间,至少5分钟。
4.4 瓶颈定位的决策树
给你一个我常用的决策思路,遇到性能问题直接套:
- 帧率不达标? → 先看trace里HAL处理时间是否超过33ms(30fps)或16ms(60fps)
- HAL处理时间正常? → 看buffer等待时间,是不是App或SurfaceFlinger在拖后腿
- buffer也正常? → 看CameraService的binder线程是否有排队,或者HIDL/AIDL调用开销异常
- 都正常? → 检查驱动层,是不是sensor的exposure时间太长,或者ISP pipeline有stall
嗯,这个决策树我用了三年,基本没失手过。你可以在项目里直接套用。
4.5 总结一下
关键路径识别,说白了就是先画图、再打点、后压测。别一上来就调代码,先搞清楚时间花在哪。我个人习惯每次优化前都先跑一遍trace,把数据存下来,优化完再跑一遍对比。这样改没改好,一目了然。
下一章咱们会深入HAL内部,看看那些占60%时间的ISP和3A处理,到底能不能再榨出几毫秒来。