2012-06-03 23 views
5

favor comparar dos formas de configuración/devuelve una matriz:forma preferida de ajuste/matrices que regresan

static public float[] test_arr_speeds_1(int a) { 
    return new float[]{ a, a + 1, a + 2, a + 3, a + 4, a + 5, 
         a + 6, a + 7, a + 8, a + 9 }; 
} // or e.g. field = new float... in method 

static public float[] test_arr_speeds_2(int a) { 
    float[] ret = new float[10]; 
    ret[0] = a; 
    ret[1] = a + 1; 
    ret[2] = a + 2; 
    ret[3] = a + 3; 
    ret[4] = a + 4; 
    ret[5] = a + 5; 
    ret[6] = a + 6; 
    ret[7] = a + 7; 
    ret[8] = a + 8; 
    ret[9] = a + 9; 
    return ret; 
} // or e.g. field[0] = ... in method 

Tanto generar códigos de bytes distintos y ambos pueden ser decompiled a su estado anterior. Después de verificar los tiempos de ejecución a través del generador de perfiles (iteraciones de 100M, entornos imparciales y diferentes), el tiempo del método _1 es de aprox. 4/3 el tiempo de _2, aunque ambos crean una nueva matriz y ambos configuran cada campo a un valor dado. Los tiempos son insignificantes la mayor parte del tiempo, pero esto todavía me molesta, ¿por qué _1 es visiblemente más lento? ¿Alguien puede verificarme/confirmarme/explicarme de una manera razonable, respaldada por JVM?

+1

¿Las representaciones de bytecode correspondientes son lo suficientemente breves como para publicarlas aquí? –

+1

Los tiempos promedio de las llamadas 1000 * 1000000 son las mismas para ambos métodos, al menos para mí. – IchBinKeinBaum

+0

estimado votante, ¿podría explicar cómo esta pregunta * no * muestra el esfuerzo de investigación y cómo es * no útil * para otros usuarios, especialmente si los votos muestran lo contrario? – vaxquis

Respuesta

6

Aquí está la diferencia entre bytecode (solo para los dos primeros elementos). Primer método:

bipush 10 
newarray float  //creating an array with reference on operand stack 

dup 
iconst_0 
iload_0 
i2f 
fastore    //setting first element 

dup 
iconst_1 
iload_0 
iconst_1 
iadd 
i2f 
fastore    //setting second element 

//... 
areturn    //returning the top of the operand stack 

Segundo método:

bipush 10 
newarray float 
astore_1   //creating an array and storing it in local variable 

aload_1 
iconst_0 
iload_0 
i2f 
fastore    //setting first element 

aload_1 
iconst_1 
iload_0 
iconst_1 
iadd 
i2f 
fastore    //setting second element 

//... 
aload_1 
areturn 

Como se puede ver, la única diferencia es que la referencia de matriz se mantiene en la pila de operando en el primer escenario (por eso dup aparece tantas veces - para evitar perder una referencia a una matriz después de fastore) mientras que en la segunda situación la referencia de la matriz se mantiene en pila normal (donde se guardan los argumentos del método y las variables locales). En este escenario, la referencia debe leerse todo el tiempo (aload_1) porque fastore requiere que el arreglo esté en la pila del operando.

No debemos hacer suposiciones basadas en este bytecode - después de todo, se traduce a las instrucciones de la CPU por y lo más probable es que en ambos casos la matriz de referencia se almacene en uno de los registros de la CPU. De lo contrario, la diferencia de rendimiento sería enorme.

Si puede medir la diferencia y lo hace optimizaciones de bajo nivel, elija la versión que sea más rápida. Pero dudo que la diferencia sea "portátil" (dependiendo de la arquitectura y la versión/implementación de JVM observará un comportamiento de temporización diferente). Dicho esto, iría por una versión más legible, en lugar de la que resulta más rápida en su computadora.

+1

análisis increíble! – AlexR

+0

Eso es exactamente lo que estaba buscando. De hecho, la diferencia parece existir solo en el uso de operando/pila normal, y estoy de acuerdo en que es demasiado bajo para molestar la mayor parte del tiempo, solo me pregunté por qué. Yo también prefiero el código legible sobre el código "optimizado". – vaxquis

Cuestiones relacionadas