收藏官网首页
查看: 10940|回复: 0

转载-编写高效的C程序与C代码优化(三)

65

主题

123

帖子

787

积分

高级会员

Rank: 4

积分
787
跳转到指定楼层
楼主
发表于 2015-9-29 10:41:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
汉枫LPB120模块
循环终止 / Loop termination

如果不加留意地编写循环终止条件,就可能会给程序带来明显的负担。我们应该尽量使用“倒数到零”的循环,使用简单的循环终止条件。循环终止条件相对简单,程序在执行的时候也会消耗相对少的时间。拿下面两个计算n!的例子来说,第一个例子使用递增循环,第二个使用递减循环。
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()循环 / Faster for() loops

这是一个简单而有效的概念,通常情况下,我们习惯把for循环写成这样:
for( i = 0;  i < 10;  i++){ ... }
i 值依次为:0,1,2,3,4,5,6,7,8,9

在不在乎循环计数器顺序的情况下,我们可以这样:
for( i = 10;  i--; ) { ... }
i值依次为: 9,8,7,6,5,4,3,2,1,0,而且循环要更快

这种方法是可行的,因为它是用更快的i--作为测试条件的,也就是说“i是否为非零数,如果是减一,然后继续”。相对于原先的代码,处理器不得不“把i减去10,结果是否为非零数,如果是,增加i,然后继续”,在紧密循环(tight loop)中,这会产生显著的区别。

这种语法看起来有一点陌生,却完全合法。循环中的第三条语句是可选的(无限循环可以写成这样for(;;)),下面的写法也可以取得同样的效果:
for(i = 10;  i;  i--){}
或者:
for(i = 10;  i != 0;  i--){}
我们唯一要小心的地方是要记住循环需要停止在0(如果循环是从50-80,这样做就不行了),而且循环的计数器为倒计数方式。

另外,我们还可以把计数器分配到寄存器上,可以产生更为有效的代码。这种将循环计数器初始化成循环次数,然后递减到零的方法,同样适用于while和do语句。

混合循环/ Loop jamming

在可以使用一个循环的场合,决不要使用两个。但是如果你要在循环中进行大量的工作,超过处理器的指令缓冲区,在这种情况下,使用两个分开的循环可能会更快,因为有可能这两个循环都被完整的保存在指令缓冲区里了。
//原先的代码
for(i = 0; i < 100; i++){
stuff();
}
for(i = 0; i < 100; i++){
morestuff();
}        
//更好的做法
for(i = 0; i < 100; i++){
stuff();
morestuff();
}
函数循环 / Function Looping

调用函数的时候,在性能上就会付出一定的代价。不光要改变程序指针,还要将那些正在使用的变量压入堆栈,分配新的变量空间。为了提高程序的效率,在程序的函数结构上,有很多工作可以做。保证程序的可读性的同时,还要尽量控制程序的大小。

如果一个函数在一个循环中被频繁调用,就可以考虑将这个循环放在函数的里面,这样可以免去重复调用函数的负担,比如:
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.
}
}
展开循环 / Loop unrolling

为了提高效率,可以将小的循环解开,不过这样会增加代码的尺寸。循环被拆开后,会降低循环计数器更新的次数,减少所执行的循环的分支数目。如果循环只重复几次,那它完全可以被拆解开,这样,由循环所带来的额外开销就会消失。

比如:
for(i = 0; i < 3; i++){
something(i);
}
//更高效的方式:
something(0);
something(1);
something(2);
因为在每次的循环中,i 的值都会增加,然后检查是否有效。编译器经常会把这种简单的循环解开,前提是这些循环的次数是固定的。对于这样的循环:
for(i = 0; i <  limit; i++) { ... }
就不可能被拆解,因为我们不知道它循环的次数到底是多少。不过,将这种类型的循环拆解开并不是不可能的。

与简单循环相比,下面的代码的长度要长很多,然而具有高得多的效率。选择8作为分块大小,只是用来演示,任何合适的长度都是可行的。例子中,循环的成立条件每八次才被检验一次,而不是每次都要检验。如果需要处理的数组的大小是确定的,我们就可以使用数组的大小作为分块的大小(或者是能够整除数组长度的数值)。不过,分块的大小跟系统的缓存大小有关。
#include<stdio.H>
#define BLOCKSIZE (8)
int 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);
  }
}
return 0;
}


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

加入Q群 返回顶部

版权与免责声明 © 2006-2024 Gizwits IoT Technology Co., Ltd. ( 粤ICP备11090211号 )

快速回复 返回顶部 返回列表