设为首页 加入收藏

TOP

编写高效的C程序与C代码优化(三)
2016-05-01 02:25:09 】 浏览:1419
Tags:编写 高效 程序 代码 优化
,像这样:

if( val == 1)     dostuff1(); else if (val == 2)     dostuff2(); else if (val == 3)     dostuff3();

使用switch可以更快:

switch( val ) {     case 1: dostuff1(); break;     case 2: dostuff2(); break;     case 3: dostuff3(); break; }

在if语句中,即使是最后一个条件成立,也要先判断所有前面的条件是否成立。Switch语句能够去除这些额外的工作。如果你不得不使用if…else,那就把最可能的成立的条件放在前面。

二分分解 / Binary Breakdown

把判断条件做成二进制的风格,比如,不要使用下面的列表:

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 */          }      }  }

慢速、低效:

c = getch(); switch(c){     case 'A': {         do something;           break;       }      case 'H': {         do something;         break;     }       case 'Z': {          do something;          break;      } } 

快速、高效:

c = getch(); switch(c) {     case 0: {         do something;         break;     }       case 1: {         do something;          break;     }      case 2: {         do something;          break;      } }

以上是两个case语句之间的比较

switch语句和查找表 / Switch statement vs. lookup tables

switch语句通常用于以下情况:

  • 调用几个函数中的一个

  • 设置一个变量或返回值

  • 执行几个代码片断中的一个

    如果case表示是密集的,在使用switch语句的前两种情况中,可以使用效率更高的查找表。比如下面的两个实现汇编代码转换成字符串的例程:

    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
              "EQ0NE0CS0CC0MI0PL0VS0VC0HI0LS0GE0LT0GT0LE00" +
               3 * condition; }

    第一个例程需要240个字节,第二个只需要72个。

    循环终止 / 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
          
            
    #define BLOCKSIZE (8) 
    int main(void) {      int i = 0;      int limit = 33;  /* could be anything */ 
        int blocklimit;      /* T
首页 上一页 1 2 3 4 5 下一页 尾页 3/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇一起talk C栗子吧( 第一百四十回.. 下一篇指针和数组的掌握以及内存的管理

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目