On 04/04/2008 at 4:40 AM, Toke Eskildsen wrote:
> On Wed, 2008-04-02 at 09:30 -0400, Mark Miller wrote:
> > > - replacement of indexed for loops with for each constructs
> > 
> > Is this always the best idea? Doesn't the for loop construct make an
> > iterator, which can be much slower than an indexed for loop?
> 
> Only in the case of iterations over collections. For arrays, the foreach
> is syntactic sugar for indexed for-loop.
> http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.14.2

I don't think this is actually true.  The text at the above-linked page simply 
says that for-each over an array "means the same as" an indexed loop over the 
same array.  "Syntactic sugar", OTOH, implies that the resulting opcode is 
exactly the same.  When I look at the byte code (using javap) for the 
comparison test I include below, I can see that the indexed and for-each loops 
do not generate the same byte code.

I constructed a simple program to compare the runtime length of the two loop 
control mechanisms, while varying the size of the array.  The test program 
takes command line parameters to control which loop control mechanism to use, 
the size of the array (#elems), and the number of times to execute the loop 
(#iters).  I used a Bash shell script to invoke the test program.

Summary of the results: over int[] arrays, indexed loops are faster on arrays 
with fewer than about a million elements.  The fewer the elements, the faster 
indexed loops are relative to for-each loops.  This could be explained by a 
higher one-time setup cost for the for-each loop - above a certain array size, 
the for-each setup cost is lost in the noise.  It should be noted, however, 
that this one-time setup cost is quite small, and might be worth the increased 
code clarity.

Here are the results for three different platforms:

  - Best of five iterations for each combination
  - All using the -server JVM option
  - Holding constant #iters * #elems = 10^10
  - Rounding the reported real time to the nearest tenth of a second
  - "% Slower" = 100 * ((For-each - Indexed) / Indexed)

Platform #1: Windows XP SP2; Intel Core 2 duo [EMAIL PROTECTED]; Java 1.5.0_13

#iters  #elems  For-each  Indexed  % Slower
------  ------  --------  -------  --------
  10^9    10^1     22.3s    13.8s       62%
  10^8    10^2     16.0s    13.6s       18%
  10^6    10^4     14.8s    13.0s       14%
  10^4    10^6     12.9s    12.9s        0%
  10^3    10^7     13.4s    13.3s        1%

Platform #2: Debian Linux, 2.6.21.7 kernel; Intel Xeon [EMAIL PROTECTED]; Java 
1.5.0_14

#iters  #elems  For-each  Indexed  % Slower
------  ------  --------  -------  --------
  10^9    10^1     33.6s    14.2s      137%
  10^8    10^2     20.4s    13.9s       47%
  10^6    10^4     19.0s    12.7s       50%
  10^4    10^6     12.7s    12.8s       -1%
  10^3    10^7     13.2s    13.2s        0%

Platform #3: Debian Linux, 2.6.21.7 kernel; Intel Xeon [EMAIL PROTECTED]; Java 
1.5.0_10

#iters  #elems  For-each  Indexed  % Slower
------  ------  --------  -------  --------
  10^9    10^1    102.7s    73.6s       40%    
  10^8    10^2    107.8s    60.0s       80%
  10^6    10^4    105.2s    58.6s       80%
  10^4    10^6     58.8s    53.0s       11%
  10^3    10^7     60.0s    54.1s       11%


----- ForEachTest.java follows -----

import java.util.Date;
import java.util.Random;

/**
 * This is meant to be called from a shell script that varies the loop style,
 * the number of iterations over the loop, and the number of elements in the
 * array over which the loop iterates, e.g.:
 * 
 *     cmd="java -server -cp . ForEachTest"
 *     for elems in 10 100 10000 1000000 10000000 ; do
 *         iters=$((10000000000/${elems}))
 *         for run in 1 2 3 4 5 ; do
 *             time $cmd --indexed --arraysize $elems --iterations $iters
 *             time $cmd --foreach --arraysize $elems --iterations $iters
 *         done
 *     done
 *
 */
public class ForEachTest {
  static String NL = System.getProperty("line.separator");
  static String usage
    = "Usage: java -server -cp . ForEachTest [ --indexed | --foreach ]"
      + NL + "\t--iterations <num-iterations>  --arraysize <array-size>";
    
  public static void main(String[] args) {
    boolean useIndexedLoop = false;
    int size = 0;
    int iterations = 0;
    try {
      for (int argnum = 0 ; argnum < args.length ; ++argnum) {
        if (args[argnum].equals("--indexed")) {
          useIndexedLoop = true;
        } else if (args[argnum].equals("--foreach")) {
          useIndexedLoop = false;
        } else if (args[argnum].equals("--iterations")) {
          iterations = Integer.parseInt(args[++argnum]);
        } else if (args[argnum].equals("--arraysize")) {
          size = Integer.parseInt(args[++argnum]);
        } else {
          throw new Exception("Unknown cmdline parameter '"
                              + args[argnum] + "'.");
        }
      }
    } catch (Exception e) {
      System.err.println("Error parsing command line: " + e + NL);
      System.err.println(usage);
      System.exit(1);
    }

    // Initial the array with random integers
    int[] array = new int[size];
    Random random = new Random();
    for (int i = 0 ; i < size ; ++i) {
      array[i] = random.nextInt();
    }
    
    int k = 0;
    if (useIndexedLoop) {
      System.out.println("Indexed : " + iterations + " iterations : "
                         + size + " elements");
      for (int m = 0 ; m < iterations ; ++m) {
        for (int j = 0 ; j < size ; ++j) {
          k = array[j];
        }
      }
    } else { // useIndexedLoop = false
      System.out.println("For-each : " + iterations + " iterations : "
                         + size + " elements");
      for (int m = 0 ; m < iterations ; ++m) {
        for (int j : array) {
          k = j;
        }
      }
    }
    k = 0;
  }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to