Local
(temporary) variables and method-argument variables are the fastest
variables to access and update. Local variables remain on the stack,
so they can be manipulated directly; the manipulation of local
variables depends on both the VM and underlying machine
implementation. Heap variables (static and instance
variables) are manipulated in heap memory through the Java
VM-assigned bytecodes that apply to these variables. There are
special bytecodes for accessing the first four local variables and
parameters on a method stack. Arguments are counted first; then, if
there are less than four passed arguments, local variables are
counted. For nonstatic methods, this
always takes
the first slot. long
s and
double
s each take two slots. Theoretically, this
means that methods with no more than three parameters and local
variables combined (four for static
methods)
should be slightly faster than equivalent methods with a larger
number of parameters and local variables. This also means that any
variables allocated the special bytecodes should be slightly faster
to manipulate. In practice, I have found any effect is small or
negligible, and it is not worth the effort involved to limit the
number of arguments and
variables.
Instance and static variables can be up to an order of magnitude slower to operate on when compared to method arguments and local variables. You can see this clearly with a simple test comparing local and static loop counters:
package tuning.exception; public class VariableTest2 { static int cntr; public static void main(String[] args) { int REPEAT = 500000000; int tot = 0; long time = System.currentTimeMillis( ); for (int i = -REPEAT; i < 0; i++) tot += i; time = System.currentTimeMillis( ) - time; System.out.println("Loop local took " + time); tot = 0; time = System.currentTimeMillis( ); for (cntr = -REPEAT; cntr < 0; cntr++) tot += cntr; time = System.currentTimeMillis( ) - time; System.out.println("Loop static took " + time); } }
Running this test results in the second loop taking several times longer than the first loop (see Table 6-4).
Table 6-4. The Cost of Nonlocal Loop Variables Relative to Local Variables
Times Relative to Loop Local Variables |
1.2 |
1.2 no JIT |
1.3 |
HotSpot 1.0 |
1.1.6 |
---|---|---|---|---|---|
Static variable time/ local variable time |
500% |
191% |
149% |
155% |
785% |
Static array element/ local variable time |
503% |
307% |
359% |
232% |
760% |
If you are making many manipulations on an instance or static variable, it is better to execute them on a temporary variable, then reassign to the instance variable at the end. This is true for instance variables that hold arrays as well. Arrays also have an overhead, due to the range checking Java provides. So if you are manipulating an element of an array many times, again you should probably assign it to a temporary variable for the duration. For example, the following code fragment repeatedly accesses and updates the same array element:
for(int i = 0; i < Repeat; i++) countArr[0]+=i;
You should replace such repeated array element manipulation with a temporary variable:
int count = countArr[0]; for(int i = 0; i < Repeat; i++) count+=i; countArr[0]=count;
This kind of substitution can also apply to an array object:
static int[] Static_array = {1,2,3,4,5,6,7,8,9}; public static int manipulate_static_array( ) { //assign the static variable to a local variable, and use that local int[] arr = Static_array; ... //or even public static int manipulate_static_array( ) { //pass the static variable to another method that manipulates it return manipulate_static_array(Static_array);} public static int manipulate_static_array(int[] arr) { ...
Array-element access is typically two to three times as expensive as accessing nonarray elements (see http://www.javaworld.com/javaworld/jw-09-1998/jw-09-speed.html). This expense is probably due to the range checking and null pointer checking (for the array itself) done by the VM. The VM JIT compiler manages to eliminate almost all the overhead in the case of large arrays. But in spite of this, you can assume that array-element access is going to be slower than plain-variable access in almost every Java environment (this also applies to array element updates). See Section 4.4 for techniques to improve performance when initializing arrays.
int
s are normally
the fastest variable type to operate on. long
s and
double
s can take longer to access and update than
other variables because they are twice the basic storage length for
Java (which is four bytes). The Java Language
Specification allows long
s and
double
s to be stored in more than one action. The
specification allows the actual manipulation of
long
s and double
s to be
implementation- and processor-dependent, so you cannot assume
long
s and double
s always take
longer. If you have one specific target environment, you can test it
to determine its implementation. Note that because of the
specification, long
s and
double
s are the only data types that can be
corrupted
by simultaneous assignment from multiple threads (see Section 10.6 for more details).
When executing arithmetic with the primitive data types,
int
s are undoubtedly the most efficient.
short
s, byte
s, and
char
s are all
widened to int
s for
almost any type of arithmetic operation. They then require a cast
back if you want to end up with the data type you started with. For
example, adding two byte
s produces an
int
and requires a cast to get back a
byte
. long
s are usually less
efficient. Floating-point arithmetic seems to be the worst.
Note that temporary variables of primitive data types (i.e., not objects) can be allocated on the stack, which is usually implemented using a faster memory cache local to the CPU. Temporary objects, however, must be created from the heap (the object reference itself is allocated on the stack, but the object must be in the heap). This means that operations on any object are invariably slower than on any of the primitive data types for temporary variables. Also, as soon as variables are discarded at the end of a method call, the memory from the stack can immediately be reused for other temporaries. But any temporary objects remain in the heap until garbage collection reallocates the space. The result is that temporary variables using primitive (nonobject) data types are better for performance.
One
other way to speed up applications is to access public instance
variables
rather than use accessor methods (getters and setters). Of course,
this breaks encapsulation, so it is bad design in most cases. The JDK
uses this technique in a number of places (e.g.,
Dimension
and
GridBagConstraints
in java.awt
have
public
instance variables; in the case of
Dimension
, this is almost certainly for
performance reasons). Generally, you can use this technique without
too much worry if you are passing an object that encapsulates a bunch
of parameters (such as GridBagConstraints
); in
fact, this makes for an extensible design. If you really want to
ensure that the object remains unaltered when passed, you can set the
instance variables to be final
(so long as it is
one of your application-defined classes).
18.218.218.230