Reordenamiento de instrucciones en Java JVM.

estaba leyendoesta entrada en el blog.

Y el autor hablaba de romper elhashCode() enString en entorno multihilo.

Por tener:

public int hashCode() {
     int h = hash;
     if (h == 0) {
         int off = offset;
         char val[] = value;
         int len = count;

         for (int i = 0; i < len; i++) {
             h = 31*h + val[off++];
         }
         hash = h;
     }
     return h;
 }

Cambiado a:

public int hashCode() {
     if (hash == 0) {
         int off = offset;
         char val[] = value;
         int len = count;

         int h = 0;
         for (int i = 0; i < len; i++) {
             h = 31*h + val[off++];
         }
         hash = h;
     }
     return hash;
 }

Lo que el autor dice y cito:

"Lo que he hecho aquí es agregar una lectura adicional:La segunda lectura de hash, antes de la vuelta.. Por extraño que parezca, y por muy improbable que sea, la primera lectura puede devolver el valor hash calculado correctamente y la segunda lectura puede devolver 0. Esto se permite bajo el modelo de memoria porque el modelo permite una reordenación extensa de las operaciones. La segunda lectura se puede mover realmente, en su código, ¡para que su procesador lo haga antes de la primera! "

Así que más allá de los comentarios, alguien dice que se puede reordenar a

int h = hash;
if (hash == 0) {
  ...
}
return h;

¿Cómo es eso posible? Pensé que reordenar solo implica mover las declaraciones del programa hacia arriba y hacia abajo. ¿Qué reglas sigue? Busqué en Google, leí las preguntas frecuentes sobre JSR133, revisé el libro de Concurrencia de Java en la práctica, pero parece que no puedo encontrar un lugar que me ayude a entender, particularmente al reordenar. Si alguien me puede orientar en la dirección correcta, realmente lo apreciaría.

Gracias a que Louis aclaró el significado de "Reordenar", no estaba pensando en términos de "byteCode"

Sin embargo, todavía no entiendo por qué está permitido mover la segunda lectura al frente, este es mi ingenuo intento de traducirlo a un formato "bytecode".

Para simplificar, las operaciones que se utilizan para calcular el código hash se expresan comocalchash(). Por lo tanto, expreso el programa como:

if (hash == 0)  {       
    h = calchash();
    hash = h;
}
return hash;

Y mi intento de expresarlo en "bytecode" forma:

R1,R2,R3 are in the operands stack, or the registers
h is in the array of local variables

En orden del programa:

if (hash == 0)  {       ---------- R1 = read hash from memory (1st read)
                        ---------- Compare (R1 == 0)
    h = calchash();     ---------- R2 = calchash()
                        ---------- h = R2 (Storing the R2 to local variable h)
    hash = h;           ---------- Hash = h (write to hash)
}
return hash             ---------- R3 = read hash from memory again(2nd read)
                        ---------- return R3

Transformación reordenada (Mi versión basada en comentarios):

                        ---------- R3 = read hash from memory (2nd read) *moved*
if (hash == 0)  {       ---------- R1 = read hash from memory (1st read)
                        ---------- Compare (R1 == 0)
    h = calchash();     ---------- R2 = calchash()
                        ---------- h = R2 (Storing the R2 to local variable h)
    hash = h;           ---------- hash = h (write to hash)
}
return hash             ---------- return R3

Revisando los comentarios otra vez, encontré esto contestado por el autor:

Transformación reordenada (del blog)

r1 = hash;
if (hash == 0) {
  r1 = hash = // calculate hash
}
return r1;

Este caso realmente funciona en un solo hilo, pero es posible fallar con varios hilos.

Parece que las JVM están haciendo simplificaciones basadas en

h = hash and it simplifies the use of R1, R2, R3 to single R1

Por lo tanto, JVM hace más que reordenar las instrucciones, también parece reducir la cantidad de registros que se utilizan.

Respuestas a la pregunta(4)

Su respuesta a la pregunta