嵌入式开发极致性能优化案例-CW32饭盒派开发板TFT刷屏为例
前言
我们之前进行了
TFT
刷屏测试确认了基本功能。刷屏速度是决定
GUI
显示帧率最根本的一环,只有优化到极致的刷屏速度,才能有基础实现更好效果的
GUI
。本篇就进行刷屏的优化,其实其思想是通用的,对于其他代码也可以参考。
1.
减少
if
条件判断
if
等条件判断会导致分支处理
,
一方面会增加指令
,
尤其是跳转指令一般执行时间比一般指令长,另外也会影响流水线和
cache
。
if(Data&0x80)
LCD_SDA_SET; //输出数据
else LCD_SDA_CLR;
改为串行操作
#define LCD_SDA_SET_VAL(val) LCD_CTRLB->BSRR=val;LCD_CTRLB->BRR=val^LCD_SDA
2.
使用寄存器变量
频繁操作的局部变量尽量使用寄存器进行缓存,避免反复从内存去加载,寄存器直接操作速度快很多。
register unsigned int data;
3.
空间换时间
8
次
for
循环改为 直接
8
次操作
其实在
memcpy
等处理中也是类似操作
,
比如连续
8
次读写组合一起,再循环。以减少
for
判断次数,也利于内部
cache
流水线处理,有一些
cpu
还有
burst
处理,这也是有利的。
inline void SPI_WriteDataF(unsigned char Data)
{
#if 0
unsigned char i=0;
for(i=8;i>0;i--)
{
if(Data&0x80)
LCD_SDA_SET; //输出数据
else LCD_SDA_CLR;
LCD_SCL_CLR;
LCD_SCL_SET;
Data<<=1;
}
#else
//LCD_SDA_LOCK;
register unsigned int data = (Data & 0x80) << 0;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x40) << 1;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x20) << 2;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x10) << 3;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x08) << 4;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x04) << 5;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x02) << 6;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
data = (Data & 0x01) << 7;
LCD_SDA_SET_VAL(data);
LCD_SCL_CLR;
LCD_SCL_SET;
//LCD_SDA_UNLOCK;
#endif
}
4.
使用内联函数减少函数跳转时间
inline void SPI_WriteDataF(unsigned char Data)
函数跳转需要时间,减少函数调用即可节约时间,尤其频繁调用的函数效果明显,但是可能增加存储空间。
5.
减少
for
循环嵌套 双重
for
嵌套改为一层
for
For
嵌套导致多重循环嵌套判断
,
浪费时间,顺序执行一般是优于分支处理的。
void Lcd_ClearF(unsigned int Color) //刷新全屏
{
unsigned int i,m;
Lcd_SetRegion(0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1);
Lcd_WriteIndex(0x2C);
for(i=0;i
{
LCD_CS_CLR;
LCD_RS_SET;
SPI_WriteDataF(Color>>8); //写入高8位数据
SPI_WriteDataF(Color); //写入低8位数据
LCD_CS_SET;
}
}
6.
减少函数调用层级
函数调用影响流水线,并且需要额外的上下文处理时间
Lcd_ClearF
中直接调用
SPI_WriteDataF
不再调用函数
LCD_WriteData_16Bit
7.
使用汇编进行优化
这个实际看情况建议先用其他方式进行优化,因为人工编写汇编代码不一定比编译器编写的好,除非非常熟悉汇编并且有明确的优化方向。
8.
速度测试
循环刷屏使用定时器记录执行多次刷屏的时间,代码见附件。
9.
编译器速度优化选项
编译器-Ofast优化
执行时间分别是
660ms, 782ms
我们优化后的代码快
15.6%
编译器-O2优化
执行时间分别是 661 ms, 908ms
我们优化后的代
码
快
快
27.2%
- 从上可以看出不管用什么编译器优化,经过上面方式 人工 优化后的代码都不差不多, 660 和 661, 说明编译器已经无法对我们优化后的代码再进行优化
- 说明我们人工优化的代码不使用编译器优化也有很好的速度性能。
-
不同的编译器优化对原来的代码影响较大
-ofast
执行时间从
908
变为了
782
。
-
哪怕是采用
-ofsat
编译器优化,我们人工优化的代码依然还有比编译器优化的代码快
15.6%,
所以编译器优化无法替代人工优化。
-
只有从设计角度去优化,避免依赖编译器优化才是根本方案。
总结
1.
优化应该从设计上去优化而不是依赖编译器,应该先找大头,优先设计原理,算法上去优化,最后采取进行汇编等底层的优化,后者成本大效果不明显不具备可移植性等,前者成本小效果明显,不依赖于编译器。
2. 建议寄存器名字和手册对应 比如 gpio 的 io 锁定寄存器,头文件中是 LOCK 手册里是 LCKR
2.
对于
IO
操作最好设置
LOCK ODR
寄存器,这样可以指定
bit
直接写值而其他位不修改,而不需要
if else
判断分别配置
BRR
和
BSRR
,可以直接操作ODR寄存器,进一步优化速度。