1、代码优化概述
(1)代码优化:分为时间优化、空间优化
(2)代码优化与程序可读性之间是相互矛盾的
(3)需要优化的代码对象:频繁使用的代码需要进行优化
例:清除从指针data开始的N个字节空间
void menclr(char *data, int N)
{
for(;N > 0; N--)
{
*data = 0;
data ++;
}
}
问题:(1)编译器是不知道N是否为0的
(2)如果处理器是32位处理器,编译器不知道data是否是4字节对齐的,
(3)编译器是不知道N是否是4的倍数的
结论:(1)编译器必须是保守的:①必须假定N的所有可能值②必须假定data所有可能的边界值
(2)了解哪些方面编译器是保守的。
(3)编写高效C代码,要求了解编译器对应的处理器体系结构
2、高效的C程序设计的途径
通过研究编译器如何把C语言代码转换成ARM汇编代码,以帮助程序员区分快速和慢速的C语言代码,进而指导程序员进行更好的C程序设计。
3、优化方式:选择正确的变量数据类型
(1)变量的数据类型
①建议采用32位ARM机器
②Load/Store不同的C数据类型,效率会有所不同
(2)例:求校验和
int checksum_v1(int *data)
{
char i;
int sum = 0;
for(i = 0; i < 64; i ++)
sum += data[i];
return sum;
}
i声明为char类型的优缺点是什么?
①chra类型的值表示范围超过了64,可以完成题目的要求
②char更节省寄存器和内存空间
上述代码对应的汇编代码为:
checksum_v1_s
MOV r2,r0 ;r2=data,地址指针
MOV r0,#0 ;sum = 0
MOV r1,#0 ; i = 0,计数器
checksum_v1_loop
LDR r3,[r2,r1,LSL #2] ;r3 = data[i]
ADD r1,r1,#1 ;r1 = i + 1
AND r1,r1,#0xff ;i = (char)r1
CMP r1,#0x40 ;compare i,64
ADD r0,r3,r0 ;sum += r3
BCC checksum_v1_loop ;if(i < 64) loop
MOV pc,r14 ;return sum
讨论:i是char类型,超过255要归零,所以汇编程序中要确保其不超过255,上述程序的汇编代码共10条指令。
(3)更改求校验和程序为:
int checksum_v2(int *data)
{
unsigned int i;
int sum = 0;
for(i = 0; i < 64; i ++)
sum += data[i];
return sum;
}
上述程序的汇编代码为:
checksum_v2_s
MOV r2,r0 ;r2=data,地址指针
MOV r0,#0 ;sum = 0
MOV r1,#0 ; i = 0,计数器
checksum_v2_loop
LDR r3,[r2,r1,LSL #2] ;r3 = data[i]
ADD r1,r1,#1 ;r1++
CMP r1,#0x40 ;compare i,64
ADD r0,r3,r0 ;sum += r3
BCC checksum_v2_loop ;if(i < 64) loop
MOV pc,r14 ;return sum
上述汇编代码共9条指令,比使用char类型少了一条指令
(4)如果要求和的数据由32位更改为16位
short checksum_v3(short *data)
{
unsigned int i;
short sum = 0;
for(i = 0; i < 64; i ++)
sum += data[i];
return sum;
}
上述程序的汇编代码为:
checksum_v3_s
MOV r2,r0 ;r2=data,地址指针
MOV r0,#0 ;sum = 0
MOV r1,#0 ; i = 0,计数器
checksum_v3_loop
ADD r3,r2,r1,LSL #1 ;r3 = &data[i]
LDRH r3,[r3,#0] ;r3=data[i]
ADD r1,r1,#1 ;r1++
CMP r1,#0x40 ;compare i,64
ADD r0,r3,r0 ;r0 = sum + r3
MOV r0,r0, LSL #16
MOV r0,r0, ASR #16 ;sum = (short)r0
BCC checksum_v3_loop ;if(i < 64) loop
MOV pc,r14 ;return sum
共12条指令,比对int型数据进行求和时的汇编代码多出3条。
讨论:对16位数据求校验和:
①LDRH指令与LDR指令不同,不支持移位地址偏移,所以需要用一条指令来单独计算地址。
②两次移位对应short数据类型,16位数据在32位系统上运算,需要进行左移、右移来得到实际的16位数。
③用data指针操作数据,避开数组。
④可以先用Int类型计算求和,计算完成后再返回short类型数据。
代码可以优化,修改为:
short checksum_v4(short *data)
{
unsigned int i;
int sum = 0;
for(i = 0; i < 64; i ++)
sum += *data++;
return (short)sum;
}
上述程序的汇编代码为:
checksum_v4_s
MOV r2,#0 ;sum = 0
MOV r1,#0 ; i = 0,计数器
checksum_v4_loop
LDRSH r3,[r0] #2 ;r3 = *(data++)
ADD r1,r1,#1 ;r1++
CMP r1,#0x40 ;compare i,64
ADD r2,r3,r2 ;sum += r3
BCC checksum_v4_loop ;if(i < 64) loop
MOV r0,r2, LSL #16
MOV r0,r0, ASR #16 ;r0 = (short)sum
MOV pc,r14 ;return r0
总结:①采用整型类型,省去了多余的移位操作,移位操作移动到了循环外
②尽量使用int类型,仅使用char、short的溢出归零特性。