性能分析工具:TouchGFX Designer 的 Performance Analyzer 与硬件定时器打点法
做界面优化,最怕什么?
怕的是凭感觉调。改个图片格式,觉得快了;换个字体,又觉得卡了。到底快了多少?卡在了哪里?全凭肉眼判断,这不行。
这一章,我们就来解决「怎么测」的问题。我会介绍两套工具:一套是 TouchGFX Designer 自带的 Performance Analyzer,另一套是我个人很依赖的硬件定时器打点法。前者适合快速看整体,后者适合深挖细节。
2.1 Performance Analyzer:开箱即用的帧率监视器
TouchGFX Designer 里藏着一个很实用的性能分析面板。你不需要额外接线,不需要改代码,点几下鼠标就能看到当前界面的刷新表现。
2.1.1 怎么打开它?
在 Designer 的右下角,找到「Performance Analyzer」标签。点击后,它会以浮动窗口的形式出现。我个人习惯把它固定在右侧,这样调试时一目了然。
USE_SIMULATOR 相关的宏定义。否则,这个窗口不会显示任何数据。
2.1.2 核心指标怎么看?
窗口里主要显示三个数据:
- FPS(帧率):当前界面每秒刷新的帧数。60 FPS 是理想值,低于 30 就会明显感觉卡顿。
- CPU Load(CPU 负载):渲染一帧占用了多少 CPU 时间。这个值越高,留给其他任务的时间就越少。
- Draw Calls(绘制调用次数):每帧向 GPU 发送了多少次绘制命令。这个数字直接反映了界面的复杂度。
我举个例子。有一次,我做一个仪表盘界面,FPS 只有 25。打开 Performance Analyzer 一看,Draw Calls 高达 180 多次。嗯,问题找到了——界面里用了太多独立的图形元素,没有做合并。后来我把几个静态元素合并成一张图片,Draw Calls 降到了 40 次,FPS 直接回到了 58。
2.1.3 它的局限性
Performance Analyzer 很好用,但它只能告诉你「整体表现如何」。它没法告诉你「具体是哪一行代码拖慢了速度」。这时候,就需要硬件定时器出马了。
2.2 硬件定时器打点法:精准到微秒的测量
硬件定时器打点法,说白了就是利用 MCU 内部的定时器,在代码的关键位置记录时间戳。通过计算两个时间戳的差值,你就能知道某段代码到底跑了多久。
这个方法我在项目里用过无数次。尤其是当 Performance Analyzer 显示 FPS 正常,但用户反馈「偶尔卡一下」的时候,硬件定时器几乎是唯一能抓到「罪魁祸首」的手段。
2.2.1 基本原理
大多数 MCU 都内置了 32 位或 64 位的定时器。我们只需要做三件事:
- 初始化一个定时器,让它以固定频率(比如 1 MHz)自由运行。
- 在要测量的代码段前后,分别读取定时器的当前值。
- 计算差值,再除以定时器频率,就得到了耗时(单位:微秒)。
代码实现非常简单。以 STM32 的 HAL 库为例:
// 初始化定时器(以 TIM2 为例,配置为 1 MHz)
void Timer_Init(void) {
__HAL_RCC_TIM2_CLK_ENABLE();
TIM2->PSC = 80 - 1; // 假设主频 80 MHz,分频后为 1 MHz
TIM2->ARR = 0xFFFFFFFF; // 最大计数值
TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器
}
// 获取当前计数值
uint32_t Timer_GetTick(void) {
return TIM2->CNT;
}
// 使用示例
void MyRenderingFunction(void) {
uint32_t start = Timer_GetTick();
// ... 你要测量的代码段 ...
uint32_t end = Timer_GetTick();
uint32_t elapsed_us = end - start; // 单位:微秒
// 可以将 elapsed_us 通过串口打印出来,或者存入缓冲区
}
2.2.2 实战:测量 TouchGFX 的渲染管线
我曾经在一个项目里,发现界面切换时有明显的「黑屏」现象。Performance Analyzer 显示 FPS 正常,但就是感觉不舒服。于是,我在 TouchGFX 的渲染管线里打了几个点:
- 点 A:进入
Screen::handleTickEvent()时 - 点 B:调用
HAL::beginFrame()后 - 点 C:调用
HAL::endFrame()前 - 点 D:退出
handleTickEvent()时
通过计算 B-A、C-B、D-C 的时间差,我很快发现:问题出在 beginFrame() 到 endFrame() 之间。进一步排查,发现是某个自定义控件在每一帧都重新创建了内部缓冲区。改成复用缓冲区后,黑屏问题彻底消失。
Timer_GetTick()。结果因为中断优先级的问题,导致时间戳乱跳。记住:打点代码要放在主循环或任务上下文里,不要在中断里做测量。
2.3 两种工具的对比与选择
说了这么多,你可能想问:到底该用哪个?
我的建议是:先用 Performance Analyzer 做宏观扫描,再用硬件定时器做微观定位。
下面这张表可以帮你快速决策:
| 对比维度 | Performance Analyzer | 硬件定时器打点法 |
|---|---|---|
| 使用难度 | 低,开箱即用 | 中,需要写少量代码 |
| 测量精度 | 帧级(约 16.7 ms 粒度) | 微秒级 |
| 定位能力 | 只能看整体 | 可以定位到函数或代码行 |
| 对系统影响 | 几乎无影响 | 极小(读取寄存器) |
| 适用场景 | 快速验证、日常调试 | 深度优化、疑难杂症排查 |
2.4 本章知识体系
为了让你更直观地理解这两套工具的关系,我画了一张流程图:
这张图的核心逻辑很简单:先用 Performance Analyzer 判断问题是否严重,如果严重,再用硬件定时器去抓「元凶」。两者配合,效率最高。