友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第219章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




(2) 寻找最致命的性能瓶颈。这也许要求一定的技巧,但所有努力都不会白费。如简单地猜测瓶颈所在,并 

试图进行优化,那么可能是白花时间。  

(3) 运用本附录介绍的提速技术,然后返回步骤 1。  

  

为使努力不至白费,瓶颈的定位是至关重要的一环。Donald Knuth'9' 曾改进过一个程序,那个程序把50% 

的时间都花在约4%的代码量上。在仅一个工作小时里,他修改了几行代码,使程序的执行速度倍增。此 

时,若将时间继续投入到剩余代码的修改上,那么只会得不偿失。Knuth 在编程界有一句名言:“过早的优 

化是一切麻烦的根源”(Premature optimization is the root of all evil )。最明智的做法是抑制过早 

优化的冲动,因为那样做可能遗漏多种有用的编程技术,造成代码更难理解和操控,并需更大的精力进行维 

护。  



D。2  寻找瓶颈  



为找出最影响程序性能的瓶颈,可采取下述几种方法:  



D。2。1  安插自己的测试代码  



插入下述“显式”计时代码,对程序进行评测:  

  

long start = System。currentTimeMillis();  

// 要计时的运算代码放在这儿  

long time = System。currentTimeMillis() start;  

  

利用System。out。println(),让一种不常用到的方法将累积时间打印到控制台窗口。由于一旦出错,编译器 

会将其忽略,所以可用一个“静态最终布尔值”(Static final boolean)打开或关闭计时,使代码能放心 

留在最终发行的程序里,这样任何时候都可以拿来应急。尽管还可以选用更复杂的评测手段,但若仅仅为了 

量度一个特定任务的执行时间,这无疑是最简便的方法。  

System。currentTimeMillis()返回的时间以千分之一秒(1 毫秒)为单位。然而,有些系统的时间精度低于 1 

毫秒(如Windows PC),所以需要重复n 次,再将总时间除以n,获得准确的时间。  



D。2。2 JDK 性能评测'2'  



JDK 配套提供了一个内建的评测程序,能跟踪花在每个例程上的时间,并将评测结果写入一个文件。不幸的 

是,JDK 评测器并不稳定。它在 JDK 1。1。1 中能正常工作,但在后续版本中却非常不稳定。  

为运行评测程序,请在调用Java 解释器的未优化版本时加上…prof 选项。例如:  

java_g …prof myClass  

或加上一个程序片(Applet):  



                                                                       679 


…………………………………………………………Page 681……………………………………………………………

java_g …prof sun。applet。AppletViewer applet。html  

理解评测程序的输出信息并不容易。事实上,在JDK 1。0 中,它居然将方法名称截短为30 字符。所以可能无 

法区分出某些方法。然而,若您用的平台确实能支持…prof 选项,那么可试试 Vladimir Bulatov 的 

 “HyperPorf”'3'或者Greg White 的“ProfileViewer ”来解释一下结果。  



D。2。3  特殊工具  



如果想随时跟上性能优化工具的潮流,最好的方法就是作一些 Web 站点的常客。比如由Jonathan Hardwick 

制作的“Tools for Optimizing Java”(Java 优化工具)网站:  

http://cs。cmu。edu/~jch/java/tools。html  



D。2。4  性能评测的技巧  



     由于评测时要用到系统时钟,所以当时不要运行其他任何进程或应用程序,以免影响测试结果。  

     如对自己的程序进行了修改,并试图(至少在开发平台上)改善它的性能,那么在修改前后应分别测 

      试一下代码的执行时间。  

     尽量在完全一致的环境中进行每一次时间测试。  

     如果可能,应设计一个不依赖任何用户输入的测试,避免用户的不同反应导致结果出现误差。  



D。3 提速方法  



现在,关键的性能瓶颈应已隔离出来。接下来,可对其应用两种类型的优化:常规手段以及依赖Java 语言。  



D。3。1  常规手段  



通常,一个有效的提速方法是用更现实的方式重新定义程序。例如,在《Programming Pearls 》(编程拾 

贝)一书中'14',Bentley 利用了一段小说数据描写,它可以生成速度非常快、而且非常精简的拼写检查 

器,从而介绍了Doug McIlroy 对英语语言的表述。除此以外,与其他方法相比,更好的算法也许能带来更大 

的性能提升——特别是在数据集的尺寸越来越大的时候。欲了解这些常规手段的详情,请参考本附录末尾的 

 “一般书籍”清单。  



D。3。2  依赖语言的方法  



为进行客观的分析,最好明确掌握各种运算的执行时间。这样一来,得到的结果可独立于当前使用的计算 

机——通过除以花在本地赋值上的时间,最后得到的就是“标准时间”。  

  

运算 示例 标准时间  

  

本地赋值 i=n; 1。0  

实例赋值 this。i=n; 1。2  

int增值 i++; 1。5  

byte 增值 b++; 2。0  

short 增值 s++; 2。0  

float 增值 f++; 2。0  

double增值 d++; 2。0  

空循环 while(true) n++; 2。0  

三元表达式 (x》2 (或2 的任意次幂) 使用更快的硬件指令  



D。3。3  特殊情况  



■字串的开销:字串连接运算符+看似简单,但实际需要消耗大量系统资源。编译器可高效地连接字串,但变 

量字串却要求可观的处理器时间。例如,假设 s和 t 是字串变量:  

System。out。println(〃heading〃 + s + 〃trailer〃 + t);  

上述语句要求新建一个 StringBuffer (字串缓冲),追加自变量,然后用toString()将结果转换回一个字 

串。因此,无论磁盘空间还是处理器时间,都会受到严重消耗。若准备追加多个字串,则可考虑直接使用一 

个字串缓冲——特别是能在一个循环里重复利用它的时候。通过在每次循环里禁止新建一个字串缓冲,可节 

省980 单位的对象创建时间(如前所述)。利用 substring()以及其他字串方法,可进一步地改善性能。如 

果可行,字符数组的速度甚至能够更快。也要注意由于同步的关系,所以 StringTokenizer 会造成较大的开 

销。  

■同步:在JDK 解释器中,调用同步方法通常会比调用不同步方法慢 10 倍。经 JIT 编译器处理后,这一性能 

上的差距提升到50 到 100倍(注意前表总结的时间显示出要慢97 倍)。所以要尽可能避免使用同步方法— 

—若不能避免,方法的同步也要比代码块的同步稍快一些。  

■重复利用对象:要花很长的时间来新建一个对象(根据前表总结的时间,对象的新建时间是赋值时间的 

980 倍,而新建一个小数组的时间是赋值时间的3100 倍)。因此,最明智的做法是保存和更新老对象的字 

段,而不是创建一个新对象。例如,不要在自己的 paint()方法中新建一个Font 对象。相反,应将其声明成 

实例对象,再初始化一次。在这以后,可在paint()里需要的时候随时进行更新。参见 Bentley 编著的《编 

程拾贝》,p。81'15' 。  

■异常:只有在不正常的情况下,才应放弃异常处理模块。什么才叫“不正常”呢?这通常是指程序遇到了 

问题,而这一般是不愿见到的,所以性能不再成为优先考虑的目标。进行优化时,将小的“try…catch”块合 

并到一起。由于这些块将代码分割成小的、各自独立的片断,所以会妨碍编译器进行优化。另一方面,若过 

份热衷于删除异常处理模块,也可能造成代码健壮程度的下降。  

■散列处理:首先,Java 1。0 和 1。1 的标准“散列表”(Hashtable)类需要造型以及特别消耗系统资源的 

同步处理(570 单位的赋值时间)。其次,早期的 JDK 库不能自动决定最佳的表格尺寸。最后,散列函数应 

针对实际使用项(Key)的特征设计。考虑到所有这些原因,我们可特别设计一个散列类,令其与特定的应用 

程序配合,从而改善常规散列表的性能。注意 Java 1。2 集合库的散列映射(HashMap)具有更大的灵活性, 

而且不会自动同步。  

■方法内嵌:只有在方法属于final (最终)、private (专用)或static (静态)的情况下,Java 编译器 

才能内嵌这个方法。而且某些情况下,还要求它绝对不可以有局部变量。若代码花大量时间调用一个不含上 

述任何属性的方法,那么请考虑为其编写一个“final”版本。  

■I/O:应尽可能使用缓冲。否则,最终也许就是一次仅输入/输出一个字节的恶果。注意 JDK 1。0 的I/O 类 

采用了大量同步措施,所以若使用象readFully()这样的一个“大批量”调用,然后由自己解释数据,就可 

获得更佳的性能。也要注意Java 1。1 的“reader”和“writer”类已针对性能进行了优化。  

■造型和实例:造型会耗去2 到 200 个单位的赋值时间。开销更大的甚至要求上溯继承(遗传)结构。其他 


返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!