在本篇文章中,我(指原作者)收集了很多经验和方法。应用这些经验和方法,可以帮助我们从执行速度和内存使用等方面来优化C语言代码。
简介
声明
哪里需要使用这些方法?
整形数
register unsigned int variable_name;
除法和取余数
Time (numerator / denominator) = C0 + C1* log2 (numerator / denominator)
= C0 + C1 * (log2 (numerator) - log2 (denominator)).
合并除法和取余数
int func_div_and_mod (int a, int b)
{
return (a / b) + (a % b);
}
通过2的幂次进行除法和取余数
typedef unsigned int uint;
uint div32u (uint a)
{
return a / 32;
}
int div32s (int a)
{
return a / 32;
}
取模的一种替代方法
uint modulo_func1 (uint count)
{
return (++count % 60);
}
uint modulo_func2 (uint count)
{
if (++count >= 60)
count = 0;
return (count);
}
使用数组下标
switch ( queue )
{
case 0 : letter = 'W';
break;
case 1 : letter = 'S';
break;
case 2 : letter = 'U';
break;
}
if ( queue == 0 )
letter = 'W';
else if ( queue == 1 )
letter = 'S';
else letter = 'U';
static char *classes="WSU";
letter = classes[queue];
全局变量
int f(void);
int g(void);
int errs;
void test1(void)
{
errs += f();
errs += g();
}
void test2(void)
{
int localerrs = errs;
localerrs += f();
localerrs += g();
errs = localerrs;
}
使用别名
void func1( int *data )
{
int i;
for(i=0; i<10; i++)
{
anyfunc( *data, i);
}
}
void func1( int *data )
{
int i;
int localdata;
localdata = *data;
for(i=0; i<10; i++)
{
anyfunc (localdata, i);
}
}
变量的生命周期分割
-
限定变量的使用数量:这个可以通过保持函数中的表达式简单、小巧、不使用太多的变量实现。将较大的函数拆分为小而简单的函数也会达到很好的效果。 -
对经常使用到的变量采用寄存器存储:这样允许我们告诉编译器该变量是需要经常使用的,所以需要优先存储于寄存器中。然而,在某种情况下,这样的变量依然可能会被分割出寄存器。
变量类型
局部变量
int wordinc (int a)
{
return a + 1;
}
short shortinc (short a)
{
return a + 1;
}
char charinc (char a)
{
return a + 1;
}
指针
void print_data_of_a_structure (const Thestruct *data_pointer)
{
...printf contents of the structure...
}
指针链
typedef struct { int x, y, z; } Point3;
typedef struct { Point3 *pos, *direction; } Object;
void InitPos1(Object *p)
{
p->pos->x = 0;
p->pos->y = 0;
p->pos->z = 0;
}
void InitPos2(Object *p)
{
Point3 *pos = p->pos;
pos->x = 0;
pos->y = 0;
pos->z = 0;
}
条件执行
int g(int a, int b, int c, int d)
{
if (a > 0 && b > 0 && c < 0 && d < 0)
// grouped conditions tied up together//
return a + b + c + d;
return -1;
}
布尔表达式和范围检查
bool PointInRectangelArea (Point p, Rectangle *r)
{
return (p.x >= r->xmin && p.x < r->xmax &&
p.y >= r->ymin && p.y < r->ymax);
}
bool PointInRectangelArea (Point p, Rectangle *r)
{
return ((unsigned) (p.x - r->xmin) < r->xmax &&
(unsigned) (p.y - r->ymin) < r->ymax);
}
布尔表达式和零值比较
int aFunction(int x, int y)
{
if (x + y < 0)
return 1;
else
return 0;
}
int sum(int x, int y)
{
int res;
res = x + y;
if ((unsigned) res < (unsigned) x) // carry set? //
res++;
return res;
}
懒检测开发
用switch()函数替代if…else…
if( val == 1)
dostuff1();
else if (val == 2)
dostuff2();
else if (val == 3)
dostuff3();
switch( val )
{
case 1: dostuff1(); break;
case 2: dostuff2(); break;
case 3: dostuff3(); break;
}
二分中断
if(a==1) {
} else if(a==2) {
} else if(a==3) {
} else if(a==4) {
} else if(a==5) {
} else if(a==6) {
} else if(a==7) {
} else if(a==8)
{
}
if(a<=4) {
if(a==1) {
} else if(a==2) {
} else if(a==3) {
} else if(a==4) {
}
}
else
{
if(a==5) {
} else if(a==6) {
} else if(a==7) {
} else if(a==8) {
}
}
if(a<=4)
{
if(a<=2)
{
if(a==1)
{
/* a is 1 */
}
else
{
/* a must be 2 */
}
}
else
{
if(a==3)
{
/* a is 3 */
}
else
{
/* a must be 4 */
}
}
}
else
{
if(a<=6)
{
if(a==5)
{
/* a is 5 */
}
else
{
/* a must be 6 */
}
}
else
{
if(a==7)
{
/* a is 7 */
}
else
{
/* a must be 8 */
}
}
}
switch语句vs查找表
-
调用一到多个函数 -
设置变量值或者返回一个值 -
执行一到多个代码片段
char * Condition_String1(int condition) {
switch(condition) {
case 0: return "EQ";
case 1: return "NE";
case 2: return "CS";
case 3: return "CC";
case 4: return "MI";
case 5: return "PL";
case 6: return "VS";
case 7: return "VC";
case 8: return "HI";
case 9: return "LS";
case 10: return "GE";
case 11: return "LT";
case 12: return "GT";
case 13: return "LE";
case 14: return "";
default: return 0;
}
}
char * Condition_String2(int condition) {
if ((unsigned) condition >= 15) return 0;
return
"EQNECSCCMIPLVSVCHILSGELTGTLE" +
3 * condition;
}
循环
循环终止
int fact1_func (int n)
{
int i, fact = 1;
for (i = 1; i <= n; i++)
fact *= i;
return (fact);
}
int fact2_func(int n)
{
int i, fact = 1;
for (i = n; i != 0; i--)
fact *= i;
return (fact);
}
更快的for()循环
for( i=0; i<10; i++){ ... }
for( i=10; i--; ) { ... }
for(i=10; i; i--){}
for(i=10; i!=0; i--){}
合并循环
函数循环
for(i=0 ; i<100 ; i++)
{
func(t,i);
}
-
-
-
void func(int w,d)
{
lots of stuff.
}
func(t);
-
-
-
void func(w)
{
for(i=0 ; i<100 ; i++)
{
//lots of stuff.
}
}
循环展开
for(i=0;i< limit;i++) { ... }
//Example 1
#include
#define BLOCKSIZE (8)
void main(void)
{
int i = 0;
int limit = 33; /* could be anything */
int blocklimit;
/* The limit may not be divisible by BLOCKSIZE,
* go as near as we can first, then tidy up.
*/
blocklimit = (limit / BLOCKSIZE) * BLOCKSIZE;
/* unroll the loop in blocks of 8 */
while( i < blocklimit )
{
printf("process(%d)n", i);
printf("process(%d)n", i+1);
printf("process(%d)n", i+2);
printf("process(%d)n", i+3);
printf("process(%d)n", i+4);
printf("process(%d)n", i+5);
printf("process(%d)n", i+6);
printf("process(%d)n", i+7);
/* update the counter */
i += 8;
}
/*
* There may be some left to do.
* This could be done as a simple for() loop,
* but a switch is faster (and more interesting)
*/
if( i < limit )
{
/* Jump into the case at the place that will allow
* us to finish off the appropriate number of items.
*/
switch( limit - i )
{
case 7 : printf("process(%d)n", i); i++;
case 6 : printf("process(%d)n", i); i++;
case 5 : printf("process(%d)n", i); i++;
case 4 : printf("process(%d)n", i); i++;
case 3 : printf("process(%d)n", i); i++;
case 2 : printf("process(%d)n", i); i++;
case 1 : printf("process(%d)n", i);
}
}
}
统计非零位的数量
//Example - 1
int countbit1(uint n)
{
int bits = 0;
while (n != 0)
{
if (n & 1) bits++;
n >>= 1;
}
return bits;
}
//Example - 2
int countbit2(uint n)
{
int bits = 0;
while (n != 0)
{
if (n & 1) bits++;
if (n & 2) bits++;
if (n & 4) bits++;
if (n & 8) bits++;
n >>= 4;
}
return bits;
}
尽早的断开循环
found = FALSE;
for(i=0;i<10000;i++)
{
if( list[i] == -99 )
{
found = TRUE;
}
}
if( found )
printf("Yes, there is a -99. Hooray!n");
found = FALSE;
for(i=0; i<10000; i++)
{
if( list[i] == -99 )
{
found = TRUE;
break;
}
}
if( found )
printf("Yes, there is a -99. Hooray!n");
函数设计
函数调用的性能消耗
int f1(int a, int b, int c, int d) {
return a + b + c + d;
}
int g1(void) {
return f1(1, 2, 3, 4);
}
int f2(int a, int b, int c, int d, int e, int f) {
return a + b + c + d + e + f;
}
ing g2(void) {
return f2(1, 2, 3, 4, 5, 6);
}
减少函数参数传递消耗
-
尽量保证函数使用少于四个参数。这样就不会使用栈来存储参数值。 -
如果函数需要多于四个的参数,尽量确保使用后面参数的价值高于让其存储于栈所付出的代价。 -
通过指针传递参数的引用而不是传递参数结构体本身。 -
将参数放入一个结构体并通过指针传入函数,这样可以减少参数的数量并提高可读性。 -
尽量少用占用两个字大小的long类型参数。对于需要浮点类型的程序,double也因为占用两个字大小而应尽量少用。 -
避免函数参数既存在于寄存器又存在于栈中(称之为参数拆分)。现在的编译器对这种情况处理的不够高效:所有的寄存器变量也会放入到栈中。 -
避免变参。变参函数将参数全部放入栈。
叶子函数
-
避免调用其他函数:包括那些转而调用C库的函数(比如除法或者浮点数操作函数)。 -
对于简短的函数使用__inline修饰()。
内联函数
__inline int square(int x) {
return x * x;
}
#include
double length(int x, int y){
return sqrt(square(x) + square(y));
}
-
没有函数调用负担。函数调用处直接替换为函数体,因此没有诸如读取寄存器变量等性能消耗。 -
更小的参数传递消耗。由于不需要拷贝变量,传递参数的消耗更小。如果参数是常量,编译器可以提供更好的优化。
使用查找表
浮点运算
-
浮点除法很慢。浮点除法比加法或者乘法慢两倍。通过使用常量将除法转换为乘法(例如,x=x/3.0可以替换为x=x*(1.0/3.0))。常量的除法在编译期间计算。 -
使用float代替double。Float类型的变量消耗更好的内存和寄存器,并由于精度低而更加高效。如果精度够用,尽可能使用float。 -
避免使用先验函数。先验函数,例如sin、exp和log是通过一系列的乘法和加法实现的(使用了精度扩展)。这些操作比通常的乘法至少慢十倍。 -
简化浮点运算表达式。编译器并不能将应用于整型操作的优化手段应用于浮点操作。例如,3*(x/3)可以优化为x,而浮点运算就会损失精度。因此,如果知道结果正确,进行必要手工浮点优化是有必要的。
其他技巧
-
尽量不在循环中使用++和–。例如:while(n–){},这有时难于优化。 -
减少全局变量的使用。 -
除非像声明为全局变量,使用static修饰变量为文件内访问。 -
尽可能使用一个字大小的变量(int、long等),使用它们(而不是char,short,double,位域等)机器可能运行的更快。 -
不使用递归。递归可能优雅而简单,但需要太多的函数调用。 -
不在循环中使用sqrt开平方函数,计算平方根非常消耗性能。 -
一维数组比多维数组更快。 -
编译器可以在一个文件中进行优化-避免将相关的函数拆分到不同的文件中,如果将它们放在一起,编译器可以更好的处理它们(例如可以使用inline)。 -
单精度函数比双精度更快。 -
浮点乘法运算比浮点除法运算更快-使用val*0.5而不是val/2.0。 -
加法操作比乘法快-使用val+val+val而不是val*3。 -
put()函数比printf()快,但不灵活。 -
使用#define宏取代常用的小函数。 -
二进制/未格式化的文件访问比格式化的文件访问更快,因为程序不需要在人为可读的ASCII和机器可读的二进制之间转化。如果你不需要阅读文件的内容,将它保存为二进制。 -
如果你的库支持mallopt()函数(用于控制malloc),尽量使用它。MAXFAST的设置,对于调用很多次malloc工作的函数由很大的性能提升。如果一个结构一秒钟内需要多次创建并销毁,试着设置mallopt选项。
原文始发于微信公众号(汇编语言):C语言代码优化的方法
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论