现在的位置: 首页 > 综合 > 正文

程序设计实践7—–性能

2013年11月08日 ⁄ 综合 ⁄ 共 2739字 ⁄ 字号 评论关闭

程序设计实践7-----性能

计时和轮廓文件

1 linux中使用time,测试程序的运行时间.或使用clock函数CC++ 提供了一个标准函数, c l o c k,它报告程序到某个时刻总共消耗的C P U时间。可以在一个函数的执行前和执行后调用c l o c k,测量C P U的使用情况.

2 使用轮廓程序。除了可靠的计时方法外,在性能分析中最重要的工具就是一种能产生轮廓文件的系统。轮廓文件是对程序在哪些地方消耗了时间的一种度量。在有些轮廓文件中列出了执行中调用的各个函数、各函数被调用的次数以及它们消耗的时间在整个执行中的百分比。另一些轮廓文件计算每个语句执行的次数。执行非常频繁的语句通常对总运行时间的贡献比较大,根本没执行的语句所指明的可能是些无用代码,或者是没有合理测试到的代码。轮廓文件是一种发现程序中执行热点的有效手段,所谓热点就是那些消耗了大部分计算时间的函数或者代码段。当然,对轮廓文件的解释也应该慎重。由于编译程序本身的复杂性、缓冲存储器和主存的复杂影响,还有做程序的轮廓文件对其本身执行所造成的影响等,轮廓文件的统计信息只能看作是近似的.轮廓文件常常可以通过编译系统的一个标志或选择项打开,然后运行程序,最后用一个分析工具显示结果。在U n i x上,这个标志一般是- p,对应的工具是p r o f

加速策略:

1 使用更好的算法或数据结构。要使程序运行速度快,最重要的因素是算法与数据结构的选择,有效的算法和不那么有效的算法造成的差距是巨大的。

2 让编译程序做优化。有一种毫不费力的改变就可能产生明显的加速效果,那就是打开编译系统的所有优化开关。现代编译程序已经做得非常好了,这实际上大大减小了程序员对程序做各种小改进的必要性。在默认情况下,CC++ 编译程序并不设法做很多优化工作,通过编译选项可以打开一个优化程序(说得更准确些,应该是“改进程序” )。按说这个选项应该是默认的,但是由于做优化会妨碍源程序排错系统的工作,所以,程序员必须在确认程序已经排错完毕之后,自己来打开优化系统。

3 调整代码。只要数据有足够的规模,算法的正确选择就会显示它的作用。进一步说,算法方面的改进是跨机器、跨系统和跨语言的。但是,如果已经选择了正确的算法,程序的速度仍然是问题的话,下一步还能做的就是调整代码,整理循环和表达式的细节,设法使事情做得更快些。

4 不要优化那些无关紧要的东西。有时所做的代码调整毫无作用,这就是因为用到了一些不能产生差异的地方。首先应该确认你优化的代码正是那些真正耗费时间的东西.

5 我们到底应该在加快程序速度方面花多少时间?最重要的标准就是看所做的改造能否带来足够的效益。作为一个准则,在加快程序速度方面所花的时间不应该超过这种加速在程序的整个生存期间中获得的时间

6 一件最重要的工作就是在做了修改之后对程序重新计时,以确认情况真正得到了改善

 

代码调整:

在发现热点之后,存在许多可以使用的能缩短运行时间的技术

1 收集公共表达式。如果一个代价昂贵的计算多次出现,那么就只在一个地方做它,并记录计算的结果。

2 如果一个计算出现在循环里,而它又不依赖任何在循环过程中改变的东西,那么就应该把它移到循环之外

3 用低代价操作代替高代价的。术语降低强度指的是用低代价操作代替高代价操作的那些优化。在过去,这个说法特别用来指用加法和移位操作取代乘法。今天再这样做,恐怕不会有太大收获了。除法和求余数比乘法慢得多,如果可以用乘倒数的方法来代替除法,或者在除数是2的幂时用掩码操作代替求余,则确实可能得到性能改进。在CC++ 里,用指针代替数组下标有可能提高速度,但是今天的大部分编译程序都已经能自动做这种优化了。

4 铺开或者删除代码。循环的准备和运行都需要一定的开销。如果循环体本身非常小,循环次数很少,有一个更有效的方法,就是把它重写为一个重复进行的计算序列

       for(int I =0; I < 3; i++)

              a[i] = b[i] + c[i];

改为:

       a0] = b[0] + c[0];

a[1] = b[1] + c[1];

a[2] = b[2] + c[2];

这样可以去掉循环的开销和特殊的分支操作,而这些都会造成执行流的中断,降低现代处理器的速度。

5 高速缓存频繁使用的值。以缓冲方式保存的值无须重新计算。缓存的价值来自于局部性。程序和人都有这种倾向,那就是重复使用最近访问过的值,或者是近旁的值,而对老的值和远距离的值则用得比较少。计算机硬件领域里广泛使用了缓存技术,给计算机增加缓冲存储器后,确实能大大地改进机器在速度方面的表现. 使用缓存技术的函数定义了一个内部的静态局部变量,用于记录前一个变量的值.

6 写专用的存储分配程序。经常可以看到这种情况,程序里的惟一热点就是存储分配,表现为对m a l l o cf r e e的大量调用。如果程序中需要的经常是同样大小的存储块,采用一个特定用途的存储分配器取代一般的分配器,有可能使速度得到实质性提高。这种特定的存储分配器调用m a l l o c一次,取得基本存储块的一个大数组,在随后需要时一次送出去一块,这是一个代价很低的操作。释放后的存储块接在一个自由表的最后,这使它们可以立即重新投入使用。

7 特殊情况特殊处理。通过使用特殊代码去操作同样大小的对象,特殊用途的分配器可以比通用分配器节约时间和空间开销,还可能减少碎片问题.

8 预先算出某些值。有时可以让程序预先计算出一些值,需要时拿起来就用,这也可能使程序运行得更快些.比如事先计算出数组的长度.

9 使用近似值。如果精度不太重要,那么就尽量使用具有较低精度的数据类型。在老的或者小的机器上,或者在那些采用软件模拟方式实现浮点数的机器上,单精度浮点运算通常比双精度运算更快一些,所以,用f l o a t而不是d o u b l e就有可能节省时间

10 在某个低级语言里重写代码。低级语言程序的效率可能更高,不过这样做也要付出代价,那就是程序员的时间. 有时,使用依赖于机器的代码也可能得到显著的加速效果。但这是最后一招,是不该轻易采用的,因为它破坏了可移植性,也使将来的维护和修改都变得更加困难.

 

空间效率:

1 使用尽可能小的数据类型以节约存储。提高空间效率的一个步骤是做些小修改,使现有存储能使用得更好,例如使用能满足工作需要的最小数据类型。比如说,如果合适的话,可以用s h o r t取代i n t,这是2 - D图形系统常用的一种技术,因为1 6位一般足以处理屏幕坐标的可能取值范围。或许是用f l o a t代替d o u b l e,这带来的潜在问题是精度损失,因为f l o a t一般只能保持67位十进制数字。

2 不存储容易重算的东西。与代码调整类似,这方面的修改也不太重要。最重要的改进应该是来自好的数据结构,或许还要伴随着算法的修改.

3 空间效率的获得往往要付出时间代价.

 

抱歉!评论已关闭.