📄 ch20.htm
字号:
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD ><I>Description</I></TD><TD WIDTH=129><I>Operation</I>
</TD><TD WIDTH=65><I><CENTER>Solaris</I></TD><TD WIDTH=93><I><CENTER>486 Win95</I>
</TD><TD WIDTH=87><CENTER><I>486 Linux</I></TD></TR>
<TR><TD WIDTH=123>Byte variable increment</TD><TD WIDTH=129><TT><FONT FACE="Courier">byte b++;</FONT></TT>
</TD><TD WIDTH=65><CENTER>1.2</TD><TD WIDTH=93><CENTER>1.2</TD><TD WIDTH=87><CENTER>1.3
</TD></TR>
<TR><TD WIDTH=123>Short variable increment</TD><TD WIDTH=129><TT><FONT FACE="Courier">short s++;</FONT></TT>
</TD><TD WIDTH=65><CENTER>1.4</TD><TD WIDTH=93><CENTER>1.2</TD><TD WIDTH=87><CENTER>1.3
</TD></TR>
<TR><TD WIDTH=123>Int variable increment</TD><TD WIDTH=129><TT><FONT FACE="Courier">int i++;</FONT></TT>
</TD><TD WIDTH=65><CENTER>0.3</TD><TD WIDTH=93><CENTER>0.1</TD><TD WIDTH=87><CENTER>0.3
</TD></TR>
<TR><TD WIDTH=123>Long variable increment</TD><TD WIDTH=129><TT><FONT FACE="Courier">long l++;</FONT></TT>
</TD><TD WIDTH=65><CENTER>1.1</TD><TD WIDTH=93><CENTER>1.1</TD><TD WIDTH=87><CENTER>1.3
</TD></TR>
<TR><TD WIDTH=123>Float variable increment</TD><TD WIDTH=129><TT><FONT FACE="Courier">float f++;</FONT></TT>
</TD><TD WIDTH=65><CENTER>0.9</TD><TD WIDTH=93><CENTER>1.1</TD><TD WIDTH=87><CENTER>1.2
</TD></TR>
<TR><TD WIDTH=123>Double variable increment</TD><TD WIDTH=129><TT><FONT FACE="Courier">double d++;</FONT></TT>
</TD><TD WIDTH=65><CENTER>1.0</TD><TD WIDTH=93><CENTER>1.3</TD><TD WIDTH=87><CENTER>1.5
</TD></TR>
</TABLE></CENTER>
<P>
<BLOCKQUOTE>
<B><CENTER>Table 20.3. The costs of miscellaneous Java operations.</CENTER></B>
</BLOCKQUOTE>
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><I>Description</I></TD><TD WIDTH=183><I>Operation</I>
</TD><TD WIDTH=65><I><CENTER>Solaris</I></TD><TD WIDTH=93><I><CENTER>486 Win95</I>
</TD><TD WIDTH=87><I><CENTER>486 Linux</I></TD></TR>
<TR><TD WIDTH=161>Object creation</TD><TD WIDTH=183><TT><FONT FACE="Courier">new Object();</FONT></TT>
</TD><TD WIDTH=65><CENTER>10.7</TD><TD WIDTH=93><CENTER>13.8</TD><TD WIDTH=87><CENTER>12.8
</TD></TR>
<TR><TD WIDTH=161>Method invocation</TD><TD WIDTH=183><TT><FONT FACE="Courier">null_func();</FONT></TT>
</TD><TD WIDTH=65><CENTER>3.1</TD><TD WIDTH=93><CENTER>2.1</TD><TD WIDTH=87><CENTER>2.4
</TD></TR>
<TR><TD WIDTH=161>Synchronized method</TD><TD WIDTH=183><TT><FONT FACE="Courier">sync_func();</FONT></TT>
</TD><TD WIDTH=65><CENTER>16.3</TD><TD WIDTH=93><CENTER>20.1</TD><TD WIDTH=87><CENTER>15.9
</TD></TR>
<TR><TD WIDTH=161>Math function</TD><TD WIDTH=183><TT><FONT FACE="Courier">Math.abs(x);</FONT></TT>
</TD><TD WIDTH=65><CENTER>5.6</TD><TD WIDTH=93><CENTER>4.8</TD><TD WIDTH=87><CENTER>5.6
</TD></TR>
<TR><TD WIDTH=161>Equivalent math code</TD><TD WIDTH=183><TT><FONT FACE="Courier">(x < 0) ? -x : x;</FONT></TT>
</TD><TD WIDTH=65><CENTER>0.6</TD><TD WIDTH=93><CENTER>0.4</TD><TD WIDTH=87><CENTER>0.6
</TD></TR>
</TABLE></CENTER>
<P>
<P>
These tables point out lots of interesting information regarding
the performance of Java code. From Table 20.1, it's readily apparent
that method variables are more efficient to use than instance
variables. Furthermore, you can see that array element assignment
is slower than method variable assignment due to the fact that
Java performs bounds checking operations whenever an array element
is accessed. Keep in mind that this table isn't meant as an argument
to get rid of all your class member data. Rather, think of it
as providing insight into making those decisions in which the
design could go either way.
<P>
Table 20.2 shows timing data relating to the use of the standard
Java data types. As you might have expected, the two 32-bit data
types, <TT><FONT FACE="Courier">int</FONT></TT> and <TT><FONT FACE="Courier">float</FONT></TT>,
showed the best performance because the tests were performed on
32-bit systems. It is interesting to note that the performance
difference between using an <TT><FONT FACE="Courier">int</FONT></TT>
over a <TT><FONT FACE="Courier">byte</FONT></TT>, <TT><FONT FACE="Courier">short</FONT></TT>,
or <TT><FONT FACE="Courier">long</FONT></TT> is much more significant
than for using a <TT><FONT FACE="Courier">float</FONT></TT> over
a <TT><FONT FACE="Courier">double</FONT></TT>.
<P>
Even though the floating-point types show comparable performance
to the integer types, don't be misled about using integer math
over floating-point math. This timing table reflects only an increment
operation, which is much different than more complex operations
performed in the context of a game. Integer math is much more
efficient than floating-point math. So use the table as a measure
of the relative speeds among integer types, and then try to use
integer math throughout your code.
<P>
Table 20.3 focuses on a few miscellaneous operations that are
worth thinking about. First, it shows the high cost of creating
an object. This should serve as an incentive to eliminate the
creation of temporary objects within a loop where the creation
occurs over and over. Rather, you can place the temporary object
above the loop and reinitialize its members as needed inside the
loop.
<P>
Table 20.3 also shows the dramatic performance costs of using
a normal method versus a synchronized method. Even though synchronization
is very important in multithreaded programming, this should be
some encouragement to minimize the usage of synchronized methods
in games.
<P>
Finally, Table 20.3 shows you how using the standard Java math
methods can sometimes be a burden. Even something as simple as
taking the absolute value of a number imposes much greater performance
costs when you call the <TT><FONT FACE="Courier">Math.abs</FONT></TT>
method, as opposed to inlining the equivalent code yourself.
<H2><A NAME="IsolatingProblemCode"><B><FONT SIZE=5 COLOR=#FF0000>Isolating
Problem Code</FONT></B></A></H2>
<P>
The biggest mistake you can make in regard to optimizing your
game code is trying to optimize <I>all</I> the code. Being smart
about what code you attack is crucial in not spending years trying
to improve the performance of your game. More important, it's
a well-established fact that a relatively small portion of code
is usually responsible for the bulk of the performance drain.
It's your job to isolate this code and then focus your optimization
efforts accordingly.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Warning</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Don't attempt to optimize code as you write it. Many programmers have the tendency to think they can do it all the first time through, which includes developing perfectly optimized error-free code. If you truly think you are capable of filling this tall
order, then be my guest. Meanwhile, the rest of us mere mortals have to contend with our fair share of mistakes, even without worrying how optimized a piece of code is. Throw in the complexities of trying to optimize code that doesn't even work yet, and
you're setting yourself up for disaster. In all fairness, it's usually okay to make minor optimizations that don't sig-nificantly impact the structure of your code; just don't get carried away.</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
Fortunately, isolating problem code isn't all that difficult if
you use the proper tools. The most useful tool in finding bottlenecks
in your code is a profiler. A profiler's job is to report on the
amount of time spent in each section of code as a program is executing.
The Java runtime interpreter has an undocumented built-in profiler
that is easy to use and works pretty well. To use the runtime
interpreter profiler, simply specify the <TT><FONT FACE="Courier">-prof</FONT></TT>
option when using the interpreter, like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">java -prof <I>Classname</I></FONT></TT>
</BLOCKQUOTE>
<P>
<TT><I><FONT FACE="Courier">Classname</FONT></I></TT> is the name
of the class you want to profile. Of course, this technique doesn't
work too well for applets, because they must be run within the
context of the applet viewer tool or a browser. Fortunately, you
can use the profiler with applets by altering the arguments to
the interpreter a little, like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">java -prof sun.applet.AppletViewer <I>Filename</I></FONT></TT>
</BLOCKQUOTE>
<P>
In this case, <TT><I><FONT FACE="Courier">Filename</FONT></I></TT>
is the name of the HTML file containing a link to your applet.
When you finish running the applet, the interpreter will write
a file named <TT><FONT FACE="Courier">java.prof</FONT></TT> to
the current directory. This file contains profile information
for the applet you just ran.
<P>
To get an idea of what kind of information the Java profiler generates,
check out Listing 20.1, which contains a few lines of profile
information generated for the Traveling Gecko sample game. I've
cleaned up the listing a little by hand just to make it easier
to read.
<HR>
<BLOCKQUOTE>
<B>Listing 20.1. A partial profile listing for the Traveling Gecko
sample game.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT>count
callee caller &nbs
p; time
<BR>
25261 java/util/Vector.size() SpriteVector.testCollision( 19
<BR>
22780 java/util/Vector.elementAt(I) SpriteVector.testCollision( 38
<BR>
20258 java/awt/Rectangle.intersects() Sprite.testCollision(LSprite;) 56
<BR>
20258 Sprite.getCollision() Sprite.testCollision(LSprite;) 4
<BR>
20258 Sprite.testCollision(LSprite;) SpriteVector.testCollision( 102
<BR>
10360 Sprite.getPosition() SpriteVector.update()V 2
<BR>
4075 java/lang/Object.<init>() java/awt/Rectangle.<init>(IIII) 0
<BR>
3800 sun/awt/image/Image.getImageRep(II) sun/awt/win32/Win32Image.getImage 60</TT>
</BLOCKQUOTE>
<HR>
<P>
As you can see, the profile information is broken down into four
columns. The first column specifies how many times a particular
method was called, and the second column states the name of the
method. The third column specifies the calling method, the one
that invoked the method in question. Finally, the fourth column
specifies the relative amount of time spent in the method during
each call. The larger this number is, the more costly the method.
<P>
You can easily use this information as a guide to determine the
code on which to focus your optimization efforts. The methods
appearing at the top of the list should receive much greater attention,
because they are being called far more times than methods farther
down in the list. Making small performance gains in a method that
is being called 20,000 times will have a much greater impact than
speeding up a method that is called only a couple of hundred times.
The cool thing is that you can try different optimizations and
then run the profiler again to see whether the relative times
have changed. This is a very practical, if somewhat time-consuming,
way to make great strides in speeding up your games.
<H2><A NAME="OptimizationTechniques"><B><FONT SIZE=5 COLOR=#FF0000>Optimization
Techniques</FONT></B></A></H2>
<P>
Now that you've isolated the code that is making your game crawl,
it's time to look into exactly what optimizations you can perform
to speed things up. The rest of today's lesson is aimed at different
techniques you can apply to code that you know could stand some
improvement. You won't always be able to optimize every piece
of problem code; the goal is to make big dents in the areas that
can be optimized.</FONT>
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Incidentally, make sure that you have already tried your game with compiler optimizations turned on (the <TT><FONT FACE="Courier">-O</FONT></TT> option). I know it's not much, but it's free!
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<H3><A NAME="RethinkAlgorithms"><B>Rethink Algorithms</B></A>
</H3>
<P>
Many C/C++ programmers have traditionally resorted to assembly
language when the issue of performance is raised. As a Java programmer,
you don't have this option. This is actually a good thing, because
it forces you to take a closer look at your design approach instead
of relying on heavier processor dependence to solve your problems.
What the assembly heads don't realize is that much more significant
gains can be made by entirely rethinking an algorithm than by
porting it to assembly. And trust me, the amount of time spent
hand-coding tedious assembly could easily result in a leaner,
more efficient algorithm.
<P>
This same ideology applies to Java programming. Before you run
off writing native methods and expanding loops to get every little
ounce of performance, which you'll learn about in a moment, take
a step back and see whether the algorithm itself has any weaknesses.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -