4、实时性瓶颈分析:CPU/GPU/DSP/ISP负载与调度

做Camera HAL这几年,我遇到最多的一个问题就是:“明明硬件参数挺高的,为什么预览就是卡?”

你想想看,手机里就那么几颗芯片,CPU、GPU、DSP、ISP,它们要抢着干活。谁抢多了,谁没活干,都会出问题。这一章,我们就来拆解一下这些处理单元的负载特征,以及怎么调度才能让它们各司其职。

4.1 四大处理单元的“性格”差异

先说个结论:没有万能的处理器。每种单元都有自己的强项和短板。

处理单元 擅长领域 典型瓶颈 我见过的坑
CPU 控制流、策略决策、少量数据 单核频率、Cache Miss 3A算法跑在大核上,结果温控降频,帧率直接腰斩
GPU 并行像素处理、图像渲染 带宽、纹理填充率 美颜滤镜用了太多片段着色器,GPU直接满载
DSP 固定算法加速(如计算摄影) 固件限制、DDR带宽 多帧降噪时DSP队列溢出,导致丢帧
ISP RAW域处理、统计信息生成 像素时钟、Line Buffer HDR合成时ISP管线超时,触发sof freeze

核心观点:实时性瓶颈往往不是单个单元算力不够,而是数据流在单元之间传递时“卡住了”。

4.2 CPU负载:别让大核“累死”

CPU在Camera HAL里主要负责什么?说白了就是调度和策略。比如3A算法、帧率控制、buffer管理。这些任务对单核性能敏感,但对多核并行度要求不高。

我个人习惯把CPU负载分成两类:

  • 前台负载:预览线程、拍照线程。这些必须跑在性能核上,延迟敏感。
  • 后台负载:统计信息处理、日志、调试。这些可以跑在效率核上,甚至绑到小核集群。

我曾经在一个项目里遇到过:预览帧率突然从30fps掉到15fps。查了半天,发现是CPU governor把3A算法线程迁移到了小核上。为什么?因为大核温度太高,内核自动做了负载均衡。结果3A算法算不过来,曝光参数更新滞后,画面忽明忽暗。

避坑指南:建议使用cgroup或sched_setaffinity将关键线程绑定到大核。我曾经因为没做这个绑定,导致某厂商的机器在高温环境下预览卡顿,被客户投诉了三次。

4.3 GPU负载:带宽才是命门

GPU在Camera里主要干两件事:图像渲染(比如预览Surface的合成)和计算(比如美颜、滤镜)。

很多人以为GPU瓶颈是算力,其实在手机Camera场景下,带宽瓶颈更常见。你想想看,一帧4K 60fps的YUV数据,带宽需求接近1.2GB/s。如果GPU还要同时做纹理采样和渲染,DDR带宽很容易被吃满。

我记得有一次调试一个“人像模式”的实时预览,背景虚化效果一打开,帧率直接掉到20fps。用perf抓了一下,发现GPU的bus_read和bus_write占比超过70%。说白了,GPU在等数据,而不是在算数据。

注意:不要盲目提升GPU频率。频率越高,带宽需求越大。如果DDR已经饱和,提频只会增加功耗,不会提升性能。

4.4 DSP负载:队列管理是关键

DSP是个好东西,专门用来跑固定算法。比如多帧降噪、HDR合成、超分辨率。它的优点是功耗低、吞吐量大。但缺点也很明显:灵活性差。

DSP的负载模型和CPU不一样。它通常有一个任务队列,HAL把算法描述符丢进去,DSP固件自己调度。如果队列满了,新的任务就会被阻塞。

我曾经遇到一个案例:某平台的多帧降噪算法,在低光场景下需要连续处理6帧。结果DSP队列深度只有4,导致第5帧和第6帧被丢弃,降噪效果大打折扣。后来我把算法拆成两批,每批3帧,中间插入一个同步点,才解决了问题。

经验之谈:DSP编程时,一定要关注队列深度和任务超时时间。我曾经因为没设置超时回调,导致DSP死锁,整个Camera HAL卡死,只能重启手机。

4.5 ISP负载:管线时序不能乱

ISP是Camera里最“硬”的部分。它的处理是流水的,每一帧从Sensor出来,经过ISP管线,生成YUV或RAW数据。这个过程中,任何一个stage超时,都会导致整帧丢失。

ISP的瓶颈通常出现在:

  • 像素时钟:Sensor输出分辨率太高,ISP处理不过来。
  • Line Buffer:某些算法(如WDR)需要多行缓存,Line Buffer不够就会stall。
  • 统计信息回读:ISP生成的AE/AWB统计信息,如果CPU来不及读取,下一帧的配置就会出错。

我记得有一次调试4K 60fps的HDR模式,ISP的sof(帧起始)信号一直正常,但eof(帧结束)信号延迟了2ms。查到最后,发现是HDR合成模块占用了太多Line Buffer,导致后面的CSC模块被阻塞。

建议:在ISP驱动中,一定要监控pixel_rate和line_time。如果发现line_time超过预期,说明ISP管线有瓶颈,需要降低分辨率或关闭某些算法模块。

4.6 调度策略:让对的人做对的事

了解了每个单元的性格,接下来就是怎么调度了。我总结了一个“三看”原则:

  1. 看延迟要求:预览、拍照这类对延迟敏感的任务,优先用CPU大核或专用硬件(ISP)。
  2. 看吞吐量要求:多帧处理、计算摄影这类对吞吐量敏感的任务,优先用DSP或GPU。
  3. 看功耗要求:低功耗场景(如视频通话),尽量用DSP或ISP,避免唤醒CPU大核。

下面这张图是我在实际项目中常用的调度决策流程:

Camera实时性调度决策流程 Camera实时性调度决策流程 帧到达 延迟敏感?(预览/拍照) 是 否 ISP / CPU大核 DSP / GPU 吞吐量够?→ 检查带宽 吞吐量够?→ 检查队列 降分辨率 / 关算法 分批处理 / 降帧率 说明:延迟敏感任务优先走ISP或CPU大核,吞吐量敏感任务优先走DSP或GPU。当资源不足时,降分辨率或关闭算法是最后的保底手段。

4.7 实战:如何定位瓶颈

说了这么多理论,最后分享一个我常用的定位方法:perf + trace。

具体步骤:

  1. 用perf stat看CPU的IPC(每时钟指令数)。如果IPC低于1,说明CPU在等数据(Cache Miss或内存延迟)。
  2. 用systrace或atrace抓取ISP和DSP的硬件事件。重点关注sof、eof、task_done这些标记。
  3. 用dumpsys查看GPU的bus_utilization。如果超过80%,说明带宽是瓶颈。

一句话总结:CPU看IPC,GPU看带宽,DSP看队列,ISP看时序。把这四个指标盯住了,实时性瓶颈基本无处遁形。