标签归档:优化程序性能

消除循环的低效率

例1:

   void test1(结构体 v)
   {
     int i;
     for(i=0;i<=getLength(v);i++)
     {
       //执行代码 
     }
   }

//在常见的代码中,我们看见这种写法,会造成每次for循环都会进行getLength操作来获取长度,所以在程序中,我们通常会进行一个改进版本,如下所示:

  void test2(结构体 v)
  {
    int i;
    int j =getLength(v);
    for(i=0;i< j;j++)
     {
       //执行代码
     }
   }

这是一种很常见的优化,称之为 “代码移动”。这类优化包括识别出要执行多次,但计算结果不会发生变化的计算,因而我们可以将计算移动到代码前面,这样,就不会被多次求值。
优化编译器会试着进行代码移动,但是编译器一般很少进行移动,因为编译器担心这样移动会带来一些副作用。


例2 代码移动分析2:

  //大写字母转小写字母
  //转换小写方式1
  void zhuanhuan1(char *s){
    int i;
    for(i =0 ;i< strlen(s);i++)
    {
     if(s[i] >='A' && s[i] <='Z')
      {
        s[i] -= ('A'-'a');
      }
   }
  } 
  //转换小写方式2
  void zhuanhuan2(char *s){
     int i;
     int len = strlen(s);
     for(i =0;i< len;i++)
      {
        if(s[i] >='A' && s[i] <='Z')
         {
           s[i] -= ('A'-'a');
         }
      }
   } 
  
 //获取长度
  size_t strlen(char *s)
   {
     int length =0;
      while (*s !=0)
      {
        s++;
        length ++;
      }
      return length;
   }
 

通过测试我们发现,当我们输入260000个字符串进行转换时,zhuanhuan1需要3分钟才能进行转换完毕;
而zhuanhuan2只需要0.03秒就可以转换完毕。

从以上两个示例我们可以看出,一个小小的代码移动就可以减少程序的执行次数,使程序得到飞一般的提速,所以我们在平常的代码中,要尽量避免类似问题的发生,使我们的程序更高效。

程序性能表示方法

在我们的程序中,我们通常需要采用一种方法来表示程序性能标志,通过程序性能标志来指导我们改进代码,对许多程序猿来说这是一种很重要的衡量标准,这种衡量标准我们称之为 每元素的周期数。这种度量标准可以更详细的级别上理解迭代程序的循环性能。
处理器活动的顺序是由时钟控制的,时钟提供了某个频率的规律信号。这个时钟信号采用MHz来表示,要么用千MHz来表示:
例:

一个系统有1.4GHz 那么就表示处理器时钟运行频率为1400兆赫兹。每个时钟周期是频率的倒数,通常采用纳秒来表示。
一个2GHz的频率的处理器,时钟周期为0.5纳秒。
500MHz的时钟周期为2纳秒

从程序开发者的角度,时钟周期来表示度量程序中单个元素的运行时常,我们可以不用去理解处理器的模型。

编译器优化的局限性

编译器运用复杂的算法来确定一个程序中计算的是什么值,以及它们是如何被使用的,然后它们会利用通过一些方法来简化表达式,也就是在几个不同的地方使用一个计算,以降低一个给定计算必须执行的次数


编译器的优化能力受以下因素限制:
1 无论编译器如何优化,编译器绝不能改变程序的最后结果
2 它们对程序的行为、对使用它们的环境了解有限
3 编译器需要快速的完成编译操作
编译器的优化工作对用户来说是不可见的。当程序员用优化选项时(-O)是生成的程序和不加入优化选项生成的程序,最终的运行结果应该都是一致,除了加入优化选项产生的程序运行速度会快一些。


例:

   //函数sum
   void sum(int *a,int *b)
    {
    *a += *b;
    *a += *b;
    }

   //函数 sum1
   void sum1(int *a,int *b)
   {
    *a = 2* *b;
   }
 

//我们发现这函数 sum 和 sum1 具有相同的程序行为,两个函数的功能都是将指针a的值由两个b的值叠加,
但是我们通过测试,我们会发现sum1函数具有更高的效率,它只需要进行三次存储器的引用就可以得到结果(读a 读 b 写 a)

而sum函数需要进行六次的读写操作(两次读a 两次读b 两次写a)


总结:

从上面的代码,我们可以看出,编译器不会进行优化将sum优化为sum1,这就情况就是我们常见的编译器的优化的局限性,这种情况就需要我们自己在程序中注意程序的写法,能编译器能够产生更高效的代码。