Java性能(转贴)
上一篇 / 下一篇 2007-01-18 09:02:46 / 个人分类:性能测试
Java语言特别强调准确性,但可靠的行为要以性能作为代价。这一特点反映在自动收集垃圾、严格的运行期检查、完整的字节码检查以及保守的运行期同步等等方面。对一个解释型的虚拟机来说,由于目前有大量平台可供挑选,所以进一步阻碍了性能的发挥。51Testing软件测试网DMSgtYo
"先做完它,再逐步完善。幸好需要改进的地方通常不会太多。"(Steve McConnell的《About performance》[16])51Testing软件测试网1qBeP B0aPp
本附录的宗旨就是指导大家寻找和优化"需要完善的那一部分"。
0oW'd$m]0\051Testing软件测试网q`7N0]&xE
1 基本方法
I$d%r}2gm0只有正确和完整地检测了程序后,再可着手解决性能方面的问题:51Testing软件测试网:l,jTJE4m,m
(1) 在现实环境中检测程序的性能。若符合要求,则目标达到。若不符合,则转到下一步。
Oj5C)gEl7o1xL0(2) 寻找最致命的性能瓶颈。这也许要求一定的技巧,但所有努力都不会白费。如简单地猜测瓶颈所在,并试图进行优化,那么可能是白花时间。
2{ M8\)qo9K(J0(3) 运用本附录介绍的提速技术,然后返回步骤1。
!^['Kf{QX7`$]0为使努力不至白费,瓶颈的定位是至关重要的一环。Donald Knuth[9]曾改进过一个程序,那个程序把50%的时间都花在约4%的代码量上。在仅一个工作小时里,他修改了几行代码,使程序的执行速度倍增。此时,若将时间继续投入到剩余代码的修改上,那么只会得不偿失。
#S@TJfG/B0Knuth在编程界有一句名言:"过早的优化是一切麻烦的根源"(Premature optimization is the root of all evil)。最明智的做法是抑制过早优化的冲动,因为那样做可能遗漏多种有用的编程技术,造成代码更难理解和操控,并需更大的精力进行维护。
i&d2]X0^;^ y,M c?4x02 寻找瓶颈51Testing软件测试网 C9hq#q,G0[6K'c%w
为找出最影响程序性能的瓶颈,可采取下述几种方法:51Testing软件测试网.lf9i2sZ%S$pU
51Testing软件测试网j8?Eek'N?O{k
1) 安插自己的测试代码
?Y+NP}CZ0A[$h"Z0插入下述"显式"计时代码,对程序进行评测:
:v3G+P:c x5Z'J0long start = System.currentTimeMillis();51Testing软件测试网_J ?$vmL.U
//要计时的运算代码放在这儿51Testing软件测试网P'BY*R&B!]
long time = System.currentTimeMillis() - start;
(V V\_(X(y0利用System.out.println(),让一种不常用到的方法将累积时间打印到控制台窗口。由于一旦出错,编译器会将其忽略,所以可用一个"静态最终布尔值"(Static final boolean)打开或关闭计时,使代码能放心留在最终发行的程序里,这样任何时候都可以拿来应急。尽管还可以选用更复杂的评测手段,但若仅仅为了量度一个特定任务的执行时间,这无疑是最简便的方法。51Testing软件测试网U*_'nn&Z
System.currentTimeMillis()返回的时间以千分之一秒(1毫秒)为单位。然而,有些系统的时间精度低于1毫秒(如Windows PC),所以需要重复n次,再将总时间除以n,获得准确的时间。51Testing软件测试网tv{5F@,U
q3P f ^A[y+x0
2) JDK性能评测[2]
LEL wYeQKG1|a0JDK配套提供了一个内建的评测程序,能跟踪花在每个例程上的时间,并将评测结果写入一个文件。不幸的是,JDK评测器并不稳定。它在JDK 1.1.1中能正常工作,但在后续版本中却非常不稳定。51Testing软件测试网.B'?Y8s,x U
为运行评测程序,请在调用Java解释器的未优化版本时加上-prof选项。例如:51Testing软件测试网Y,W9\IE!_}
java_g -prof myClass51Testing软件测试网 @6r\E(Lr*T
或加上一个程序片(Applet):
d&^o N/P1c.@"@l0java_g -prof sun.applet.AppletViewer applet.html51Testing软件测试网~g+dR~EL*E
理解评测程序的输出信息并不容易。事实上,在JDK 1.0中,它居然将方法名称截短为30字符。所以可能无法区分出某些方法。然而,若您用的平台确实能支持-prof选项,那么可试试Vladimir Bulatov的"HyperPorf"[3]或者Greg White的"ProfileViewer"来解释一下结果。51Testing软件测试网#Y'JQ|)s
51Testing软件测试网2bRLoc!ox8E\ob
3) 特殊工具
*aJt6_p0x-An `0如果想随时跟上性能优化工具的潮流,最好的方法就是作一些Web站点的常客。比如由Jonathan Hardwick制作的"Tools for Optimizing Java"(Java优化工具)网站:51Testing软件测试网2caSKCU
http://www.cs.cmu.edu/~jch/java/tools.html51Testing软件测试网ds9`0j!gn4j
8A6[a s(\Y0
4) 性能评测的技巧
1r,`(K|8I0■由于评测时要用到系统时钟,所以当时不要运行其他任何进程或应用程序,以免影响测试结果。
8K&Q!?(~"ML V w(s0■如对自己的程序进行了修改,并试图(至少在开发平台上)改善它的性能,那么在修改前后应分别测试一下代码的执行时间。51Testing软件测试网G)k Z-_iz1o#R3[
■尽量在完全一致的环境中进行每一次时间测试。
y0f1E&JR|1X`M0■如果可能,应设计一个不依赖任何用户输入的测试,避免用户的不同反应导致结果出现误差。51Testing软件测试网5ue"u%Xb @ Yi
fO:f ?#u+?r0
3 提速方法51Testing软件测试网 K(iw!DZ
现在,关键的性能瓶颈应已隔离出来。接下来,可对其应用两种类型的优化:常规手段以及依赖Java语言。51Testing软件测试网9e1K3B5G u
j9KZV/ng]0
1) 常规手段51Testing软件测试网;D*~sB&W
通常,一个有效的提速方法是用更现实的方式重新定义程序。例如,在《Programming Pearls》(编程拾贝)一书中[14],Bentley利用了一段小说数据描写,它可以生成速度非常快、而且非常精简的拼写检查器,从而介绍了Doug McIlroy对英语语言的表述。除此以外,与其他方法相比,更好的算法也许能带来更大的性能提升--特别是在数据集的尺寸越来越大的时候。欲了解这些常规手段的详情,请参考本附录末尾的"一般书籍"清单。
SL2PL!KkT0,{ ~U6fgR9k"U0
2) 依赖语言的方法
V_BsExC4I.` hH0为进行客观的分析,最好明确掌握各种运算的执行时间。这样一来,得到的结果可独立于当前使用的计算机--通过除以花在本地赋值上的时间,最后得到的就是"标准时间"。51Testing软件测试网Ap1jo2I?
to `Fw#a)^:cl0
运算示例标准时间51Testing软件测试网1^'l)T"^+\!r1bK
51Testing软件测试网d cs1^&^ g&s
本地赋值i=n; 1.051Testing软件测试网NzH0G0r0E5iM s
实例赋值this.i=n; 1.2
3q9?'y7tR.Y0int增值i++; 1.551Testing软件测试网}!w+W:I })L%bl
byte增值b++; 2.0
k4~B0z-Ok/j:k0short增值s++; 2.051Testing软件测试网xzH8q],L
float增值f++; 2.0
WKQ Q#u5S0double增值d++; 2.0
sRR~8u&v(b0空循环while(true) n++; 2.051Testing软件测试网&{po(y%[H |El#G;L
三元表达式(x<0) ?-x : x 2.2
+{ T:lf.^"\J2d0算术调用Math.abs(x); 2.551Testing软件测试网;f[5X@:N.|hG1hT`2i
数组赋值a[0] = n; 2.751Testing软件测试网n3nHx9L6{t1CR
long增值l++; 3.5
;b,~x,jq0方法调用funct(); 5.9
.tg L?4uA0throw或catch异常try{ throw e; }或catch(e){} 32051Testing软件测试网8~ y.e@J]
同步方法调用synchMehod(); 57051Testing软件测试网Sni:C8Ah}&n x/V
新建对象new Object(); 980
8P*e+fo `"Y|U0新建数组new int[10]; 3100
5|6Fl"]@?QVw%W0([xg5P;W:}Gu(T0
通过自己的系统(如我的Pentium 200 Pro,Netscape 3及JDK 1.1.5),这些相对时间向大家揭示出:新建对象和数组会造成最沉重的开销,同步会造成比较沉重的开销,而一次不同步的方法调用会造成适度的开销。参考资源[5]和[6]为大家总结了测量用程序片的Web地址,可到自己的机器上运行它们。
h@D_;oN+k07D^(j8LN%K0
1.常规修改51Testing软件测试网)k1yL:f9T| vNL
下面是加快Java程序关键部分执行速度的一些常规操作建议(注意对比修改前后的测试结果)。
x V$Jj&TF+\2A2] O051Testing软件测试网6lW8zJ7Q-q,dq
将...修改成...理由
7tA%_/G E"ag7Q%B051Testing软件测试网{0QWZC&lvc
接口抽象类(只需一个父时)接口的多个继承会妨碍性能的优化51Testing软件测试网.@l0yM{%t'gX
非本地或数组循环变量本地循环变量根据前表的耗时比较,一次实例整数赋值的时间是本地整数赋值时间的1.2倍,但数组赋值的时间是本地整数赋值的2.7倍51Testing软件测试网X&_8Uvh
链接列表(固定尺寸)保存丢弃的链接项目,或将列表替换成一个循环数组(大致知道尺寸)每新建一个对象,都相当于本地赋值980次。参考"重复利用对象"(下一节)、Van Wyk[12] p.87以及Bentley[15] p.8151Testing软件测试网6^k(Rf8TG*l:B
x/2(或2的任意次幂)X>>2(或2的任意次幂)使用更快的硬件指令51Testing软件测试网il+g.A$o
51Testing软件测试网AOQ$BG#|
3) 特殊情况
_7Bw*])Qk0■字串的开销:字串连接运算符+看似简单,但实际需要消耗大量系统资源。编译器可高效地连接字串,但变量字串却要求可观的处理器时间。例如,假设s和t是字串变量:
M*[1CH3G0System.out.println("heading" + s + "trailer" + t);
3`1LlN%t%h0上述语句要求新建一个StringBuffer(字串缓冲),追加自变量,然后用toString()将结果转换回一个字串。因此,无论磁盘空间还是处理器时间,都会受到严重消耗。若准备追加多个字串,则可考虑直接使用一个字串缓冲--特别是能在一个循环里重复利用它的时候。通过在每次循环里禁止新建一个字串缓冲,可节省980单位的对象创建时间(如前所述)。利用substring()以及其他字串方法,可进一步地改善性能。如果可行,字符数组的速度甚至能够更快。也要注意由于同步的关系,所以StringTokenizer会造成较大的开销。51Testing软件测试网y.D+E p:?c
■同步:在JDK解释器中,调用同步方法通常会比调用不同步方法慢10倍。经JIT编译器处理后,这一性能上的差距提升到50到100倍(注意前表总结的时间显示出要慢97倍)。所以要尽可能避免使用同步方法--若不能避免,方法的同步也要比代码块的同步稍快一些。
qd8Y:x n-]0■重复利用对象:要花很长的时间来新建一个对象(根据前表总结的时间,对象的新建时间是赋值时间的980倍,而新建一个小数组的时间是赋值时间的3100倍)。因此,最明智的做法是保存和更新老对象的字段,而不是创建一个新对象。例如,不要在自己的paint()方法中新建一个Font对象。相反,应将其声明成实例对象,再初始化一次。在这以后,可在paint()里需要的时候随时进行更新。参见Bentley编著的《编程拾贝》,p.81[15]。
5X#Qk b.{0■异常:只有在不正常的情况下,才应放弃异常处理模块。什么才叫"不正常"呢?这通常是指程序遇到了问题,而这一般是不愿见到的,所以性能不再成为优先考虑的目标。进行优化时,将小的"try-catch"块合并到一起。由于这些块将代码分割成小的、各自独立的片断,所以会妨碍编译器进行优化。另一方面,若过份热衷于删除异常处理模块,也可能造成代码健壮程度的下降。
(FV q e_C{8c0散列处理:首先,Java 1.0和1.1的标准"散列表"(Hashtable)类需要造型以及特别消耗系统资源的同步处理(570单位的赋值时间)。其次,早期的JDK库不能自动决定最佳的表格尺寸。最后,散列函数应针对实际使用项(Key)的特征设计。考虑到所有这些原因,我们可特别设计一个散列类,令其与特定的应用程序配合,从而改善常规散列表的性能。注意Java 1.2集合库的散列映射(HashMap)具有更大的灵活性,而且不会自动同步。
5Qe:b ?R.r]S0方法内嵌:只有在方法属于final(最终)、private(专用)或static(静态)的情况下,Java编译器才能内嵌这个方法。而且某些情况下,还要求它绝对不可以有局部变量。若代码花大量时间调用一个不含上述任何属性的方法,那么请考虑为其编写一个"final"版本。
v:A:_ b3I,[-q0I/O:应尽可能使用缓冲。否则,最终也许就是一次仅输入/输出一个字节的恶果。注意JDK 1.0的I/O类采用了大量同步措施,所以若使用象readFully()这样的一个"大批量"调用,然后由自己解释数据,就可获得更佳的性能。也要注意Java 1.1的"reader"和"writer"类已针对性能进行了优化。51Testing软件测试网 rx;S@j,F:e2F
造型和实例:造型会耗去2到200个单位的赋值时间。开销更大的甚至要求上溯继承(遗传)结构。其他高代价的操作会损失和恢复更低层结构的能力。
}?Dt/\&_0图形:利用剪切技术,减少在repaint()中的工作量;倍增缓冲区,提高接收速度;同时利用图形压缩技术,缩短下载时间。来自JavaWorld的"Java Applets"以及来自Sun的"Performing Animation"是两个很好的教程。请记着使用最贴切的命令。例如,为根据一系列点画一个多边形,和drawLine()相比,drawPolygon()的速度要快得多。如必须画一条单像素粗细的直线,drawLine(x,y,x,y)的速度比fillRect(x,y,1,1)快。
?B&`d|c%~3B N3N%H Q0使用API类:尽量使用来自Java API的类,因为它们本身已针对机器的性能进行了优化。这是用Java难于达到的。比如在复制任意长度的一个数组时,arraryCopy()比使用循环的速度快得多。51Testing软件测试网 t9QDfvi l6D`T7^
替换API类:有些时候,API类提供了比我们希望更多的功能,相应的执行时间也会增加。因此,可定做特别的版本,让它做更少的事情,但可更快地运行。例如,假定一个应用程序需要一个容器来保存大量数组。为加快执行速度,可将原来的Vector(矢量)替换成更快的动态对象数组。51Testing软件测试网'a;MD\ cvg
51Testing软件测试网J(X*s"P7j]4l]
1.其他建议51Testing软件测试网:o1O7j R,rD9~;WR*B6@
将重复的常数计算移至关键循环之外--比如计算固定长度缓冲区的buffer.length。51Testing软件测试网D x)]c:T }H
static final(静态最终)常数有助于编译器优化程序。
BXQ$v O O$p&|9S0实现固定长度的循环。
}/I,r ^rS {s|0w0使用javac的优化选项:-O。它通过内嵌static,final以及private方法,从而优化编译过的代码。注意类的长度可能会增加(只对JDK 1.1而言--更早的版本也许不能执行字节查证)。新型的"Just-in-time"(JIT)编译器会动态加速代码。
OX/j ^Fujd&KO H0尽可能地将计数减至0--这使用了一个特殊的JVM字节码。
zM;yoRfu4Y-l0c(SJ+Cp\0
4 参考资源51Testing软件测试网'E5{4g p"K?R.w&_X
z/c)d^H*t&Qc0
1) 性能工具
M4Kc&J CQFx0[1]运行于Pentium Pro 200,Netscape 3.0,JDK 1.1.4的MicroBenchmark(参见下面的参考资源)51Testing软件测试网t?-t%H~ Y@
[2] Sun的Java文档页--JDK Java解释器主题:
9o'V8V!i!Q"}0http://java.sun.com/products/JDK/tools/win32/java.html
.~$T,?6~,r p8ej)Q0[3] Vladimir Bulatov的HyperProf51Testing软件测试网UDXtr/~d \k
http://www.physics.orst.edu/~bulatov/HyperProf51Testing软件测试网4y3VJAK.@%zHL
[4] Greg White的ProfileViewer51Testing软件测试网1W^*R6lXy
http://www.inetmi.com/~gwhi/ProfileViewer/ProfileViewer.html51Testing软件测试网;\%J NUZ8SK([:AX
51Testing软件测试网gR:K-j"Rd*R
2) Web站点
3@f|qn#Mp0[5]对于Java代码的优化主题,最出色的在线参考资源是Jonathan Hardwick的"Java Optimization"网站:
H;_ \3J`$`0http://www.cs.cmu.edu/~jch/java/optimization.html51Testing软件测试网ElP}q+Gh{
"Java优化工具"主页:
!MkGPcO.| ? b0http://www.cs.cmu.edu/~jch/java/tools.html51Testing软件测试网qu#D~ ||\)@ s%F-v4o
以及"Java Microbenchmarks"(有一个45秒钟的评测过程):51Testing软件测试网7up'Gs'lj
http://www.cs.cmu.edu/~jch/java/benchmarks.html51Testing软件测试网1}fx$_w#J$D
51Testing软件测试网{s%aB)R1P
3) 文章
/yQT8W-^9UD0[6] "Make Java fast:Optimize! How to get the greatest performanceout of your code through low-level optimizations in Java"(让Java更快:优化!如何通过在Java中的低级优化,使代码发挥最出色的性能)。作者:Doug Bell。网址:
!\!_vu(W ]3N&WgN@0http://www.javaworld.com/javaworld/jw-04-1997/jw-04-optimize.html51Testing软件测试网@S` u(rJ2b
(含一个全面的性能评测程序片,有详尽注释)51Testing软件测试网U)mw$cQ-fq%N;l+t&F
[7] "Java Optimization Resources"(Java优化资源)
zrI5v.C\0http://www.cs.cmu.edu/~jch/java/resources.html
/a/F)VI9]e GR ~0[8] "Optimizing Java for Speed"(优化Java,提高速度):51Testing软件测试网8F1d%gt*y
http://www.cs.cmu.edu/~jch/java/speed.html
9Rg0G'w|{*b9l0[9] "An Empirical Study of FORTRAN Programs"(FORTRAN程序实战解析)。作者:Donald Knuth。1971年出版。第1卷,p.105-33,"软件--实践和练习"。
)jN~ Q1kcv0]V0[10] "Building High-Performance Applications and Servers in Java:An Experiential Study"。作者:Jimmy Nguyen,Michael Fraenkel,RichardRedpath,Binh Q. Nguyen以及Sandeep K. Singhal。IBM T.J. Watson ResearchCenter,IBM Software Solutions。
:{0~a]8~3@s6R0http://www.ibm.com/java/education/javahipr.html51Testing软件测试网o.u&j"s/? F\
51Testing软件测试网ul#j`@
4) Java专业书籍
llM-O_A9MX0[11]《Advanced Java,Idioms,Pitfalls,Styles, and Programming Tips》。作者:Chris Laffra。Prentice Hall 1997年出版(Java 1.0)。第11章第20小节。51Testing软件测试网p]o J'M"F8j{1j:[
+c;bc(b${0
5) 一般书籍
d_dB+z(l0[12]《Data Structures and C Programs》(数据结构和C程序)。作者:J.Van Wyk。Addison-Wesly 1998年出版。
t{o"X\0[13]《Writing Efficient Programs》(编写有效的程序)。作者:Jon Bentley。Prentice Hall 1982年出版。特别参考p.110和p.145-151。
c(~l8R4i,a0[14]《More Programming Pearls》(编程拾贝第二版)。作者:JonBentley。"Association for Computing Machinery",1998年2月。
0GicWWx2kN1FQ0[15]《Programming Pearls》(编程拾贝)。作者:Jone Bentley。Addison-Wesley 1989年出版。第2部分强调了常规的性能改善问题。[16]《Code Complete:A Practical Handbook of Software Construction》(完整代码索引:实用软件开发手册)。作者:Steve McConnell。Microsoft出版社1993年出版,第9章。51Testing软件测试网"ub-y@#q
[17]《Object-Oriented System Development》(面向对象系统的开发)。作者:Champeaux,Lea和Faure。第25章。51Testing软件测试网Q|B%c @t'}
[18]《The Art of Programming》(编程艺术)。作者:Donald Knuth。第1卷"基本算法第3版";第3卷"排序和搜索第2版"。Addison-Wesley出版。这是有关程序算法的一本百科全书。
5{:{6|:XB,L^0[19]《Algorithms in C:Fundammentals,Data Structures, Sorting,Searching》(C算法:基础、数据结构、排序、搜索)第3版。作者:RobertSedgewick。Addison-Wesley 1997年出版。作者是Knuth的学生。这是专门讨论几种语言的七个版本之一。对算法进行了深入浅出的解释。
e] ZZ3N#z*G1T051Testing软件测试网Z1h-w:h*L:_8x
源文档 <http://www.51testing.com/html/8/465.html>
+A)]Deo~5yag"g0相关阅读:
- 开发出高性能的网站 (一) 20个客户端代码优化技巧 (pele, 2007-1-08)
- 开发出高性能的网站 (二) — 最佳缓冲控制 (pele, 2007-1-08)
- 开发出高性能的网站,(三):压缩和其他服务器端的技术 (pele, 2007-1-08)
- AAA服务器的性能测试 (miyukenshin, 2007-1-11)
- 详解loadrunner的think time(转贴) (yexu, 2007-1-18)
- LoadRunner的一个解决方案 (转贴) (yexu, 2007-1-18)
- 性能测试常见误区(转贴) (yexu, 2007-1-18)
- 性能测试(并发负载压力)测试分析-简要篇(转贴) (yexu, 2007-1-18)
- 性能测试及性能调整概述(转贴) (yexu, 2007-1-18)
- 性能测试之协议分析(转贴) (yexu, 2007-1-18)
TAG: 性能测试
标题搜索
日历
|
|||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
1 | |||||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 | |||
9 | 10 | 11 | 12 | 13 | 14 | 15 | |||
16 | 17 | 18 | 19 | 20 | 21 | 22 | |||
23 | 24 | 25 | 26 | 27 | 28 | 29 | |||
30 | 31 |
数据统计
- 访问量: 42161
- 日志数: 61
- 建立时间: 2007-01-15
- 更新时间: 2007-07-23