micro-non-optimisations: variables and loops

Geek

(No, not a Stereolab track.)

I will admit it, I am an optimisation junkie. I have this noise in the back of my head when I am hacking on code, much like badgerbadgerbadger but instead "optimiseoptimiseoptimise". It is hard to ignore and has made me develop some weird habits; like always declaring the variables used inside a loop on the outside of the loop.

It struck me recently that this is exactly the sort of thing that a compiler should be thinking about, not me. So to check it out, I wrote a few Java test classes, compiled them, dumped the resulting bytecode to text files and waded through Java bytecode for the first time in my life.

Here is the code for the two test classes I used:

import java.util.Collection;
import java.util.Iterator;

public class Test1 {

    public void testMethod(Collection c) {
        Iterator i = c.iterator();
        Object o;
        while (i.hasNext()) {
            o = i.next();
        }
    }

}

and:

import java.util.Collection;
import java.util.Iterator;

public class Test2 {

    public void testMethod(Collection c) {
        Iterator i = c.iterator();
        while (i.hasNext()) {
            Object o = i.next();
        }

    }

}

Note that the two are identical, except for the class name and where the variable o is declared. Test1 is how I would normally write it, Test2 is how a normal person might.

I compiled the classes using javac from Sun's JDK 1.4.2. The result? it actually surprised me: Absolutely nothing. Except for the differences in the class and file names, the bytecode generated (or at least the opcodes that javap was showing me) were identical. I was expecting at least one or two minor differences, but there was nought.

An article at IBM's DeveloperWorks proved to be interesting reading, and explained (well, pointed out the bleeding obvious) why there were no differences between the two.

Every time a method is called the JVM creates a frame for it and adds it to the stack. Each frame has an array to hold method arguments and local variables - the local variable table. Looking at the code above, when the method is first invoked, the local variable table would contain a reference to this at position 0 and a reference to the collection c at position 1. The iterator obtained from the collection is is stored in position 2. When inside the loop, the variable o is stored in position 3. Not much else needs to happen, a fact that is reflected in the compiled bytecode.

For the curious, this is the output from javap for the test method from Test1:

  Code:
   Stack=1, Locals=4, Args_size=2
   0:	aload_1
   1:	invokeinterface	#2,  1; //InterfaceMethod java/util/Collection.iterator:()Ljava/util/Iterator;
   6:	astore_2
   7:	aload_2
   8:	invokeinterface	#3,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   13:	ifeq	26
   16:	aload_2
   17:	invokeinterface	#4,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   22:	astore_3
   23:	goto	7
   26:	return

It is pretty easy to see how the process I described above is actually done. Each frame also has an operand stack that is used to supply operands for instructions and to hold the result of executing an instruction. The test method repeatedly loads references onto the operand stack, executes an instruction (invokeinterface in this case) and stores the result. At offset 0 and 1, the collection is pushed and getIterator() is called. The resulting iterator replaces the collection on the operand stack and at offset 6 is popped and stored in the local variable table. This is repeated for the hasNext() and next() calls.

So is there any point declaring the variables outside the loop for reasons of speed? I doubt it. If you need to access a variable both inside a loop and afterwards, then of course you will have to, but otherwise there seems to be little point. When I have some more time, I will do some more tests to see if I can prod javac into doing something different. But from now on, I am going to stop declaring variables outside the loop. Hopefully my code will be a little easier to read and a little less buggy as a result.

Posted Tuesday, September 27, 2005 at 23:55.

TrackBacks

TrackBack URL for this post: http://volition.vee.net/mt/mt-idle-trackback.cgi/516

Comments

I'm glad that you took the time to check if it made any difference where the variable is declared. I have never bothered to try and find out even though I have wondered if it actually made a difference. For the record, I always declare the vars in the loop as it makes the code easier to read. Not to mention that its the compilers job to do that type of optimisation AFAIK. Have you ever read the java specialists newsletters Mike?

Posted by: shaun on September 28, 2005 03:09 PM

You had me at 'micro-non-optimisations'.

Posted by: on September 28, 2005 03:25 PM

Yes. It's good that you're looking at the bytecode - and not just because a bunch of commonly-seen "optimisations" are eventually seen to be illusory %)

I tend to like it when people do that though, if only because it shows they care (or at least think) about it.

Having said that, assuming one's compiler is going to optimise away one's code, either by accident or design, has been known to send one's code-reviewers into fits of coughing.

*cough*

Posted by: Joel on September 28, 2005 08:22 PM

MichaelJGratton.

How have you not blogged about the death of Maxwell Smart? Surely at least as momentous as that of Derridude and Supermang!

Posted by: ann on September 29, 2005 03:42 AM

Shaun, no, never read it. Do you? Have you got a URL?

Joel, dude, does this mean you're doing code reviews these days instead of actual hacking? Say it isn't so!

Ann, mang, yes I did note and mourn his passing, but I felt that there was nothing more I could add that others have not already. That was the second saddest day ever.

Posted by: Mike on October 3, 2005 12:14 PM

Yes. Yes, it does.

Up until a few months ago I was doing both, and that was OK. Now... well, now I'm actively looking for another job.

Posted by: Joel on October 3, 2005 11:41 PM

PS - thanks for reminding me badgerbadgerbadger.com existed; you probably saved a few young lives last week ;-/

Posted by: Joel on October 3, 2005 11:42 PM

Mike, link to the newsletters. I was subscribed but now I just use firefox live bookmarks instead.
http://www.javaspecialists.co.za/archive/archive.html

Posted by: shaun on October 4, 2005 10:19 AM

Add a Comment



(Optional)


(Optional)


Preview your comment before submitting.