Primera vez se ejecuta un bucle Java LENTO, ¿por qué? [Sun HotSpot 1.5, sparc]

Al comparar algunos códigos Java en un cuadro Solaris SPARC, noté que la primera vez que llamo a la función de evaluación comparativa se ejecuta EXTREMADAMENTE lento (10x de diferencia):

Primero | 1 | 25295.979 msSegundo | 1 | 2256,990 msThird | 1 | 2250.575 ms

¿Por qué es esto? Sospecho que el compilador JIT, ¿hay alguna forma de verificar esto?

Editar A la luz de algunas respuestas, quería aclarar que este código es el caso de prueba más simple posible que pueda encontrar exhibiendo este comportamiento. Por lo tanto, mi objetivo no es hacer que se ejecute rápido, sino comprender lo que está sucediendo para poder evitarlo en mis puntos de referencia reales.

Resuelto: Tom Hawtin señaló correctamente que mi tiempo "LENTO" era realmente razonable. Después de esta observación, adjunté un depurador al proceso de Java. Durante el primero, el bucle interno se ve así:

0xf9037218:     cmp      %l0, 100
0xf903721c:     bge,pn   %icc,0xf90371f4        ! 0xf90371f4
0xf9037220:     nop
0xf9037224:     ld       [%l3 + 92], %l2
0xf9037228:     ld       [%l2 + 8], %l6
0xf903722c:     add      %l6, 1, %l5
0xf9037230:     st       %l5, [%l2 + 8]
0xf9037234:     inc      %l0
0xf9037238:     ld       [%l1], %g0
0xf903723c:     ba,pt    %icc,0xf9037218        ! 0xf9037218

En las siguientes iteraciones, el bucle se ve así:

0xf90377d4:     sub      %l2, %l0, %l3
0xf90377d8:     add      %l3, %l0, %l2
0xf90377dc:     add      %l2, 1, %l4
0xf90377e0:     inc      %l0
0xf90377e4:     cmp      %l0, 100
0xf90377e8:     bl,pn    %icc,0xf90377d8        ! 0xf90377d8

So HotSpot eliminó los accesos de memoria del bucle interno, acelerándolo en un orden de magnitud.

Lección ¡Haz las matematicas! Debería haber hecho el cálculo de Tom yo mismo.

Código de referencia de Java:

    private int counter;
    private int nThreads;

    private void measure(String tag) throws Exception {
            MyThread threads[] = new MyThread[nThreads];
            int i;

            counter = 0;

            for (i = 0; i < nThreads; i++)
                    threads[i] = new MyThread();

            long start = System.nanoTime();

            for (i = 0; i < nThreads; i++)
                    threads[i].start();

            for (i = 0; i < nThreads; i++)
                    threads[i].join();

            if (tag != null)
                    System.out.format("%-20s | %-2d | %.3f ms \n", tag, nThreads,
                                     new Double((System.nanoTime() - start) / 1000000.0));
    }
    public MyBench() {
            try {
                    this.nThreads = 1;
                    measure("First");
                    measure("Second");
                    measure("Third");
            } catch (Exception e) {
                    System.out.println("Error: " + e);
            }
    }

    private class MyThread extends Thread {
            public void run() {
                    while (counter < 10000000) {
                            // work
                            for (int j = 0; j < 100; j++)
                                    counter++;
                            counter -= 99;
                    }
            }
    }

Respuestas a la pregunta(14)

Su respuesta a la pregunta