23、性能分析与优化:QNX System Profiler使用、Android Systrace与Perfetto、驱动延迟分析、CPU/内存/IO瓶颈定位

性能分析这事儿,说白了就是“找茬”。

你写的驱动跑起来了,功能也正常,但系统就是卡顿、掉帧、响应慢。这时候就得靠工具说话。我个人习惯,先把问题定性——是CPU吃满了?内存不够了?还是IO卡住了?定性之后再定量,用数据说话。

23.1 QNX System Profiler:实时系统的“显微镜”

QNX是硬实时系统,它的性能分析工具和Linux那套不太一样。QNX System Profiler(也叫qconn + IDE Profiler)是我在项目中用得最多的工具。

它能看什么?

  • 每个线程的CPU占用率(精确到微秒级)
  • 线程调度延迟(谁抢占了谁)
  • 中断响应时间(ISR到线程的切换)
  • 内核调用耗时(MsgSend、SyncCondWait等)

我曾经遇到一个诡异的问题:车载仪表盘的数字指针偶尔会卡一下。用System Profiler一抓,发现是一个后台日志线程每隔500ms抢占了UI渲染线程。嗯,优先级设错了。

核心思路:QNX的调度是优先级抢占式的。高优先级线程如果跑得太久,低优先级线程就饿死了。用Profiler看“抢占次数”和“就绪队列长度”,一眼就能看出问题。

23.2 Android Systrace与Perfetto:从“黑盒”到“白盒”

Android Automotive上,Systrace是老牌工具,Perfetto是新一代。我建议直接上Perfetto,它把Systrace、ftrace、heapprofd、perf都整合了。

Perfetto的核心概念:

  • Trace点:内核和用户态埋的标记点
  • Track:按进程/线程组织的时序图
  • Slice:一个函数的执行区间
  • Counter:CPU频率、内存占用等连续数值

举个例子,你怀疑驱动在某个ioctl调用里卡住了。用Perfetto抓一下,看那个ioctl的slice是不是特别长。如果长,再点进去看它调用了哪些内核函数。

我的小技巧:抓Perfetto时,记得加上--long-trace参数,默认只抓5秒,有时候不够。我一般抓30秒,让问题复现。

23.3 驱动延迟分析:从ISR到用户态的完整链路

驱动延迟,不只是中断响应慢。它分好几段:

  1. 硬件延迟:外设信号到CPU引脚的时间
  2. 中断响应延迟:CPU响应IRQ到进入ISR
  3. ISR执行时间:ISR本身跑多久
  4. 线程调度延迟:ISR唤醒的线程何时被调度
  5. 线程执行时间:业务逻辑处理耗时

我在项目中遇到过,一个SPI驱动的总延迟从1ms飙到了10ms。用QNX System Profiler一看,问题出在第4段——ISR唤醒的线程优先级不够,被其他线程抢占了。把优先级从20提到25,延迟立刻降回1.2ms。

注意:别盲目提优先级。高优先级线程如果跑死循环,整个系统就挂了。我一般用“优先级继承”或“优先级天花板”协议来避免死锁。

23.4 CPU瓶颈定位:谁在吃CPU?

CPU跑满100%不一定是坏事,但如果是某个驱动线程占用了90%,那肯定有问题。

定位步骤:

  • toppidin(QNX)看CPU占用率
  • 用Perfetto看哪个线程的Slice最密集
  • perfgprof看热点函数

我曾经遇到一个CAN驱动,CPU占用率高达40%。用perf一看,热点在memcpy——原来驱动里每次接收报文都拷贝了4KB数据,而CAN报文只有8字节。改成零拷贝后,CPU占用降到2%。

经验之谈:CPU瓶颈往往不是算法复杂,而是“做了不该做的事”。比如频繁的上下文切换、不必要的内存拷贝、轮询代替中断。

23.5 内存瓶颈定位:谁在泄漏?谁在抖动?

内存问题分两种:泄漏和抖动。

泄漏:内存只增不减。用valgrindheapprofd(Perfetto内置)抓堆内存分配。看哪个函数分配了内存没释放。

抖动:内存频繁分配释放,导致碎片和GC压力。用Perfetto的heapprofd看分配频率。我曾经在多媒体驱动里发现,每帧图像都new一个buffer,用完就delete。改成对象池后,帧率从24fps提升到60fps。

我的习惯:在QNX上,我用showmem命令看进程内存。在Android上,我用dumpsys meminfo。但最直观的还是Perfetto的Memory track,能看到每个进程的PSS、RSS变化曲线。

23.6 IO瓶颈定位:谁在等磁盘?谁在等网络?

IO瓶颈最隐蔽。CPU没跑满,内存也够,但系统就是慢。这时候看IO。

QNX上:iostat看磁盘读写速率和等待队列长度。如果await(平均等待时间)很高,说明磁盘是瓶颈。

Android上:用Perfetto的Ftrace跟踪block_rq_insertblock_rq_complete事件。看IO请求从发起到完成花了多久。

我遇到过一个问题:车载导航启动慢。用Perfetto一看,启动过程中有大量随机读,磁盘IOPS打满了。把数据库文件从ext4换到f2fs,启动时间从8秒降到3秒。

注意:IO瓶颈不一定是磁盘慢。也可能是文件系统锁竞争、page cache命中率低、或者IO调度策略不对。我建议先看iostat -x 1,如果%util接近100%,再深入分析。

23.7 实战:一个完整的性能分析流程

说了这么多,来个实战流程。假设你发现车载仪表盘在切换界面时卡顿。

  1. 抓Perfetto:抓30秒trace,包含CPU、调度、内存、IO事件
  2. 看CPU:切换瞬间CPU有没有飙到100%?哪个线程在跑?
  3. 看调度:UI线程有没有被抢占?谁抢占了它?
  4. 看内存:有没有频繁的GC?有没有内存分配?
  5. 看IO:有没有文件读写?有没有等待磁盘?
  6. 定位根因:比如发现是某个日志驱动在写文件,导致IO阻塞。关掉日志,问题解决。

记住:性能分析不是一次性的。改完一个点,再抓一次trace,确认问题是否真的解决了。有时候修了一个瓶颈,另一个瓶颈就冒出来了。

23.8 知识体系总览

下面这张图,是我自己总结的性能分析知识体系。从工具到指标,从定位到优化,一条线串起来。

性能分析知识体系 工具层 QNX System Profiler Android Perfetto 指标层 CPU占用 / 调度延迟 内存PSS / 分配频率 瓶颈定位 CPU瓶颈 / 内存瓶颈 IO瓶颈 / 调度瓶颈 优化手段 零拷贝 / 优先级调整 对象池 / 文件系统调优 从工具到指标,从定位到优化,形成闭环

23.9 总结

性能分析,说白了就是“测量-分析-优化-验证”的循环。工具只是手段,关键是思路。

我见过很多工程师,拿到Perfetto trace就懵了,不知道看哪里。我的建议是:先看CPU,再看调度,最后看IO和内存。从最可能出问题的地方入手。

嗯,今天就聊到这儿。工具用熟了,问题自然就藏不住了。

公众号:蓝海资料掘金营,微信deep3321