性能优化-编码优化(C语言)


性能优化-编码优化(C语言)不涉及设计上的性能优化,针对语言coding层级的,函数编写级别的技巧;(里面部分优化技能没有经过验证)

整体思路与误区

当前编译器的优化其实已经做了很多工作,很多时候我们想当然的任务更优的代码,实际上在编译器的优化下,它的汇编指令基本一致的。
个人的理解,在编码过程中以可读性优先,没有必要为了性能牺牲可读性,适当权衡性能编码方式(很多性能优化的写法一定程度上都影响了可读性、可维护性)。如果是热点代码,频繁调用的代码 可以侧重一些,如果本身代码执行就不是性能主要路径,那么我建议还是可读性优先。 如果希望做性能优化,还是建议在热点路径上有测试数据支持的情况下针对性优化

总而言之,在保持良好可读性条件下添加一些高性能编码习惯,个人认为才是合理方式编译器优化功能对那些平铺直叙的代码更有效,避免在编码里面加入一些想当然的”花招“,这反而会影响编译器优化。
(性能优化优先级:系统设计》数据结构/算法 选择》热点代码编码调整)

函数调用优化

减少函数的参数,无需返回值就明确定义void

函数入参低于一定数量(如4个),则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于的部分必须通过堆栈进行传递,性能降低;用指针传递的效率高于结构赋值;直接用全局变量省去了传递时间,但是影响了模块化和可重入,要慎重使用;
如果函数不需要返回值就明确void;

异常分支独立函数
避免小函数调用开销(提炼宏函数 或 inline内联化)
int afunc(char *buf, bool enable) {
	if (check_null(buf) == true) {
          return -1;
    }
}
// 优化为:
#define check_null(a) if (a==null) { return -1;}
int afunc(char *buf, bool enable) {
	check_null(buf); // 宏函数,降低调用开销
}

分支预测优化

安排判断的顺序,让运行最快和最可能发生的分支首先被执行。

likely/unlikely
if语句的预测
switch-case分支预测
条件判断次序

变量与内存访问优化

减少不必要的赋值/变量初始化 和 不必要临时变量

减少不必要的赋值或者变量初始化,减少不必要的临时变量;

int i = 0;
i = input_value;
// 优化:
int i = input_value
int ret = do_next_level_fuc();
return ret;
// 优化:
return do_next_level_fuc();
尽可能使用const常量

const常量如果声明的对象地址不被获取,允许编译器不对他分配存储空间,可以生成更高效的代码;

减少全局变量的访问

全局变量的使用会妨碍编译器的优化行为,不要到处都是全局变量直接访问,应该收敛到一个固定的入口层次,内部的函数都应该对全局变量不直接可见。

register变量使用

register变量可以建议编译器把该变量放置在寄存器而不是堆栈上,CPU可以更快的读写访问;

结构体布局对齐/本地变量排布布局

很多编译器都有 数据结构 双字或者四字对齐配置,分配给结构体成员空间顺序也可能和声明的不一致。但是部分编译器不提供这些功能或者效果不好。一般情况对齐原则struct大小规则(默认情况,未指定对齐编译命令下):1、每个成员偏移量为其成员size整数倍,若不是在其前插入字节填充;2、所有成员计算完成后,总大小必须是最大成员整数倍,若不是在尾部添加字节填充;
建议:1、把长类型放在短类型前面,避免内存空洞;2、补齐结构体为最长成员整数倍;(本地变量排序类似)

struct{
	char a[5];//存在空洞
	long b;
	double c;
} type_a;
// 优化为:
struct{
	double c;
	long b;
	char a[5];
	char reserve[7];
} type_b;
复杂结构体变量间直接赋值比memcpy效率高

循环体优化

循环展开,降低循环层次或者次数
while (i < count) {
	a[i] = i;
	i++
}
// 优化为:
while (i < count - 1) {
	a[i] = i;
	a[i+1]=i+1;
	i+=2;
}
if (i == count - 1) {
	a[count - 1] = count - 1;
}
循环合并(计数器相同的),避免多次轮询
if (i = 0; i < index; i++) {
	do_type_a_work(i);
}
if (i = 0; i < index; i++) {
	do_type_b_work(i);
}
// 优化为:
if (i = 0; i < index; i++) {
	do_type_a_work(i);
	do_type_b_work(i);
}
循环内计算外提(每次计算不变),降低无效计算
for (int i = 0; i < get_max_index(); i++) {} 
// 优化为:
int max_index = get_max_index()for (int i = 0; i < max_index; i++) {} 
循环内多级寻址外提,避免反复寻址跳转
for (int i = 0; i < max_index; i++) {
	ainfo->bconfig.cset[i].index = index;
	ainfo->bconfig.cset[i].flag = flag;
}
// 优化:
set = ainfo->bconfig.cset;
for (int i = 0; i < max_index; i++) {
	set[i].index = index;
	set[i].flag = flag;
}
循环内判断外提(某时刻结果不变),降低无效比较次数
if (i = 0; i < index; i++) {
	if (type==TYPE_A) {
		do_type_a_work(i);
	} else {
		do_type_b_work(i);
    }
}
// 优化:
if (type==TYPE_A) { // 提高性能的同时,影响了可维护性;
	if (i = 0; i < index; i++) {
		do_type_a_work(i);
	}
} else {
	if (i = 0; i < index; i++) {
		do_type_b_work(i);
	}
}
循环体使用int类型
多重循环 最忙的循环放最里面
for (column = 0; column < 100; column ++) {
	for (row = 0; row < 5; row++) {
		sum += table[row][column ];
	}
}
// 优化
for (row = 0; row < 5; row++) {
	for (column = 0; column < 100; column ++) {
		sum += table[row][column ];
	}
}
方向不敏感的循环采用递减取代递增

大部分MCU都有为0转移的指令,把index和0比较,编译器更容易优化,执行速度也更快;

for (int i = 0; i < 1000; i++) {}
// 优化为:
for (int i = 1000; i > 0; i--) {}

数学计算优化

使用加减位移替代乘除(个人不建议)
a /= 2; b *= 2; c *= 5;
// 优化
a >> 1; b << 1; c = c << 2 + c;

(个人不建议,非热点代码使用这些技巧强烈反对:1、影响可读性 2、很多时候编译器本身优化就做得很好(经常优化后汇编代码完全一致的))

用位与替代取余操作
if (a%2 == 1) { }// 对齐判断
num += 4 - num % 4; //对齐操作
// 优化
 if ((a & 1) ==1) {};
 num = (num + 3) & 0xfffffffc;
使用自增自减替代+1,-1
x=x+1;
//优化为:
x++;

性能更高,大部分cpu指令无需读取,增加,写回,而是直接增量操作符执行;

提取计算过程公共表达式,避免重复计算,特别是除法

用低级别语言重写代码

简化复杂逻辑(其他)

用查表法替代复杂逻辑
缓存操作:减少实时性不高的函数调用及消息

参考资料

《代码大全》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值