Loading ...
Sorry, an error occurred while loading the content.
 

Re: Code Cleanup Guidelines - Discussion

Expand Messages
  • thpr
    ... I need to be precise for a moment to ensure we are all talking the same conditions. The case I m referring to isn t object creation (e.g. new)
    Message 1 of 9 , Aug 20, 2009
      --- In pcgen_developers@yahoogroups.com, Andrew Wilson <andrew@...> wrote:
      > I'm with James here, I would have thought that defining the variables
      > outside the loop would be more efficient.


      I need to be precise for a moment to ensure we are all talking the same conditions. The case I'm referring to isn't object creation (e.g. new) inside/outside of a loop, it's local variable definition (type blah; or type blah = throwaway;). If all you are doing is allocating the field and you rewrite it before you use it every time, then prior to compiler optimization, it's a penalty to pull it outside. After compiler optimization they are probably equal (but inside is more clear on intent, IMHO)


      Case under discussion:
      int printint = 0; //This solely extracts the definition of "printint"
      for (int i = 0; i < 10; i++)
      {
      printint = i + 1;
      ...
      }

      Note the initialization to zero is unconditionally forgotten (useless load), which is a hint that something is amiss.

      If there is actual object creation (or a method call or whatever) that is duplicated inside the FOR loop, then if the object (or answer from the method) does not change, it does make sense (from a performance point of view) to extract. Curtis has already pointed this out.

      Examples of things I'm NOT talking about:

      Method extracted:

      int baseint = doComplexStuff();
      for (int i = 0; i < 10; i++)
      {
      int printInt = baseint + i;
      ...
      }

      Construction extracted:

      Point2D.Double p = new Point2D.Double();
      for (int i = 0; i < 10; i++)
      {
      p.setLocation(i, i + 3.2);
      ...
      }




      For the case under discussion, the performance balance toward an outside definition exists in languages where new memory allocation (can be expensive) is triggered when items are first defined (and would therefore duplicate the effort if the definition is inside a FOR loop). It also depended on how well the compiler can optimize, and this has changed in the last 10-20 years. Thus, pulling definitions out of loops is a habit for many developers, especially those moving to Java from C. Declaring the variable outside the scope in C was a necessary compiler hint at one point in time, but I have to stand corrected that it matters anymore (this is what I get for no longer developing in C!). I found a demonstration that the results are identical: http://stackoverflow.com/questions/982963/is-there-any-overhead-to-declaring-a-variable-within-a-loop-c
      (one of the comments shows the compiled result with gcc 4.0)

      Note that the compiled code allocates it outside the loop for a C program regardless of where it was in the source code.

      > Is there a handy internet
      > reference that explains why it isn't?

      The above page shows the case in C under gcc 4.0, but generally, none that I know. I find a lot of people have assumptions about what is higher performance (and you can see it on the page linked above), but many of those assumptions are provably wrong. If you want to demonstrate a difference, get a disassembler and *prove it*.

      I can demonstrate how inside the FOR is more efficient in Java by showing it in the bytecode :)

      A reminder that I am talking about the byte code, and I make no guarantees what happens once HotSpot or the JIT compiler gets ahold of it. It's distinctly possible the result is the same. Also a warning: YMMV may vary with other languages, especially interpreted ones. Disassemblers are your friend when you're optimizing at this level.

      On to the facts for Java:

      Java statically calculates the maximum stack and local variable count required in a method and stores that in the .class file. (see http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html ) Therefore, only one local variable memory allocation takes place for the entire method, when the method is invoked (see http://cs.nyu.edu/courses/fall02/V22.0201-001/jvm5.html ). This single memory allocation per method invocation eliminates the relationship between the location in a method that a variable definition takes place and the number of memory allocations for a method invocation.

      Note this refers to local variables (which may simply be an object identifier), not to the actual objects/arrays that may be referred to by those object identifiers [this is getting in the land of pointers, which is something Java has abstracted developers away from, but which is actually relevant to memory allocation inside of a method]. For example, with Object o = new Object(); and JFrame f = new JFrame(); each consumes 1 local variable and that is what is allocated at method invocation for methods containing that code... the actual memory for the new Object or JFrame (obviously very different in size!) is allocated at construction (part of "new"). It is simply a reference/pointer to that object (the "object identifier") which is stored in the local variable [the reference is predictable as 32- or 64-bit, based on the JVM running the byte code]

      If you read the bytecode (produced by Eclipse 3.3 or Sun's Java 5 Compiler), there are 2 bytecode effects to pulling definitions outside the FOR:
      (1) The original assignments [to false] are unnecessary bytecode (wastes time and bytes in the .class file) since they are thrown away/overwritten before use and
      (2) The (unused once the FOR is done) values stay on the local variable list and may force the local variable count to be larger than otherwise necessary [If you had 2 FOR loops, one with a String and the other with an Integer, for example, the maximum # of local variables required in the method is actually 1 higher with the definitions outside the FOR vs. inside the FOR; example below].

      Also, as a result of the changed scope, if the target is an object, it will also delay garbage collection of that object (since garbage collection is possible once something leaves scope, a larger scope naturally means later garbage collection)

      Using these example classes:
      public class OutsideFor
      {
      public static void main(String[] args)
      {
      int printint = 0;
      for (int i = 0; i < 10; i++)
      {
      printint = i + 1;
      System.out.println(printint);
      }
      String printstr = null;
      for (int i = 0; i < 10; i++)
      {
      printstr = "PR" + i;
      System.out.println(printstr);
      }
      }
      }


      public class InsideFor
      {
      public static void main(String[] args)
      {
      for (int i = 0; i < 10; i++)
      {
      int printint = i + 1;
      System.out.println(printint);
      }
      for (int i = 0; i < 10; i++)
      {
      String printstr = "PR" + i;
      System.out.println(printstr);
      }
      }
      }


      Once you compile, you can open those class files up in jclasslib:
      http://www.ej-technologies.com/products/jclasslib/overview.html

      For each variable moved outside the FOR, you will find an additional "store" and "load" combination (Methods//[0] Main/[1] Code on the left side, then byte code is on the Bytecode tab), plus the extra local variable due to the increased scope in the examples above (on the Misc tab)

      TP.

      (p.s. since we're on the topic of performance, bonus points if you can tell whether Eclipse or Sun's compiler produces faster byte code - they are different, at least with the versions I am using)
    Your message has been successfully submitted and would be delivered to recipients shortly.