///Use Stack Variables Whenever Possible

Use Stack Variables Whenever Possible

Use stack variables whenever possible

 

Editor’s note: The following articles are excerpts from the book "Practical Java" published by Addison-Wesley. You can order this book from Borders.com.

When frequently accessing variables, you need to consider from where they are accessed. Is the variable static , on the stack, or an instance variable of a class? Where you store a variable has a significant impact on the performance of code that accesses it. For example, consider the following code:

class StackVars

{
private int instVar;
private static int staticVar;

//Stack variable access
void stackAccess(int val)
{
int j=0;
for (int i=0; i<val; i++)
j += 1;
}

//Class instance variable access
void instanceAccess(int val)
{
for (int i=0; i<val; i++)
instVar += 1;
}

//Class static variable access
void staticAccess(int val)
{
for (int i=0; i<val; i++)
staticVar += 1;
}
}

Each method in this code executes the same loop for the same number of iterations. They differ only in that each loop increments a different type of variable. The method stackAccess increments a local stack variable, instanceAccess increments a class instance variable, and staticAccess increments a class static variable.

instanceAccess and staticAccess take about the same amount of time to execute. stackAccess , however, is two to three times faster. Accessing a stack variable is so much faster because the JVM performs less work than when accessing static or class instance variables. Look at the generated bytecode for the three methods:



Method void stackAccess(int)
0 iconst_0 //Push 0 onto the stack.
1 istore_2 //Pop 0 and store it at index 2 of the local
//variable table(j).
2 iconst_0 //Push 0.
3 istore_3 //Pop 0 and store it at index 3 of the local
//variable table(i).
4 goto 13 //Jump to location 13.
7 iinc 2 1 //Increment j stored at index 2 by 1.
10 iinc 3 1 //Increment i stored at index 3 by 1.
13 iload_3 //Push the value at index 3(i).
14 iload_1 //Push the value at index 1(val).
15 if_icmplt 7 //Pop i and val. Jump to location 7 if i is
//less than val.
18 return //Return to calling method.

Method void instanceAccess(int)
0 iconst_0 //Push 0 onto the stack.
1 istore_2 //Pop 0 and store it at index 2 of the local
//variable table(i).
2 goto 18 //Jump to location 18.
5 aload_0 //Push index 0(this).
6 dup //Duplicate the top stack value and push it.
7 getfield #19 <Field int instVar>
//Pop the object reference for this and push
//the value for instVar.
10 iconst_1 //Push 1.
11 iadd //Pop the top two values, push their sum.
12 putfield #19 <Field int instVar>
//Pop the top two values and store the sum
//in instVar.
15 iinc 2 1 //Increment i stored at index 2 by 1.
18 iload_2 //Push the value at index 2(i).
19 iload_1 //Push the value at index 1(val).
20 if_icmplt 5 //Pop i and val. Jump to location 5 if i is
//less than val.
23 return //Return to calling method.

Method void staticAccess(int)
0 iconst_0 //Push 0 onto the stack.
1 istore_2 //Pop 0 and store it at index 2 of the local
//variable table(i).
2 goto 16 //Jump to location 16.
5 getstatic #25 <Field int staticVar>
//Push the value from the constant pool for
//staticVar.
8 iconst_1 //Push 1.
9 iadd //Pop the top two values, push their sum.
10 putstatic #25 <Field int staticVar>
//Pop the value for sum and store it in
//staticVar.
13 iinc 2 1 //Increment i stored at index 2 by 1.
16 iload_2 //Push the value at index 2(i).
17 iload_1 //Push the value at index 1(val).
18 if_icmplt 5 //Pop i and val. Jump to location 5 if i is
//less than val.
21 return //Return to calling method.

Looking at the bytecode reveals why stack variables are more efficient. The JVM is a stack-based machine and therefore is optimized to access and manipulate stack data. All local variables are stored in a local variable table and manipulated on the Java operand stack and can be accessed very efficiently. Accessing static and instance variables is more costly because the JVM must use a more expensive opcode and access them from the constant pool. (The constant pool holds symbolic references to all types, fields, and methods used by a type.)

Typically, after the first access of a static or instance variable from the constant pool, the bytecode is dynamically changed by the JVM to use a more efficient opcode. Regardless of this optimization, stack variables are still faster to access.

Given these facts, the previous code can be restructured to operate more efficiently by accessing stack variables instead of instance or static variables. Consider the modified code:

class StackVars

{
//As before...
void instanceAccess(int val)
{
int j = instVar;
for (int i=0; i<val; i++)
j += 1;
instVar = j;
}

void staticAccess(int val)
{
int j = staticVar;
for (int i=0; i<val; i++)
j += 1;
staticVar = j;
}
}

The methods instanceAccess and staticAccess are modified to copy their instance or static variables to a local stack variable. When manipulation of a variable is complete, the value is copied back to the instance or static variable. This simple change significantly improves the performance of instanceAccess and staticAccess . The execution times of the three methods are now effectively equal, with instanceAccess and staticAccess executing only about 4 percent slower than stackAccess .

This does not mean you should avoid using static or instance variables. You should use whatever storage mechanism makes sense for your design. For example, if you access static or instance variables in a loop, you can significantly improve the performance of the code by temporarily storing them in a local stack variable. This provides a more efficient sequence of bytecode instructions for the JVM to execute.

2010-05-26T17:15:53+00:00 July 24th, 2003|Java|0 Comments

About the Author:

Leave A Comment