Consider the following code:
public class test {
public void f(int i, int j) {
}
public void g() {
f(6, 6) ;
}
}
When this is compiled and disassembled we find that the call to f()
comes out as:
aload_0 bipush 6 bipush 6 invokevirtual test/f(II)V
bipush 6 is a two byte instruction, consisting of the hex opcode
0x10 followed by the constant byte value 6.
The 8-bit constant is sign extended to a 32-bit int and pushed onto the
stack. Replacing the second bipush with a dup will
have the same effect, but saves one byte.
aload_0 bipush 6 dup invokevirtual test/f(II)V
The bipush instruction can only be used for constant values
which fit in a signed 8-bit integer (-128 to 127). Larger constants
require the use of either the sipush, ldc
or ldc_w instructions. sipush is a three
byte instruction: opcode followed by a 16-bit signed integer constant.
For larger values still ldc and ldc_w must be used.
These take one or two byte indices into the constant pool of the current
class to identify the quantity to be pushed onto the stack. The item
in the constant pool can be an int, a float or a String.
When we find two identical sipush, ldc or
ldc_w instructions next to one another we can always replace
the second by a dup to save one byte in the class file.
The same technique can be applied to the ldc2_w instruction,
which pushes a two-word constant (long int or double) onto the stack. In
this case, though, we must use the dup2 instruction instead of
dup, to duplicate the two-word value on the stack.
In the initial example above I deliberately used the constant value 6
rather than a smaller integer. There are special one-byte instructions to
push the values -1, 0, 1, 2, 3, 4 and 5. Replacing one of these with a
dup won't give any saving.
I have measured the time taken to perform a million calls to the
method g() for different values of the arguments, then
subtracted the time to perform the same number of calls to a method
taking no arguments. The measurements were made using JDK 1.0.2 on
a 133 MHz Pentium running Linux 2.0.33. Here are the results:
Constant Bytes Unoptimised Optimised
saved time (ms) time (ms)
1 0 311 362
6 1 407 409
146 2 486 446
100,000 1 431 414
Clearly there is no advantage in replacing the special instructions for small integer constants: the class file is no smaller and the modified code runs more slowly. In all other cases there is some advantage to be gained from the optimisation. The improvement is particularly marked for two-byte constants (e.g. 146).