2012-06-03 2 views
5

favore confronta due modi di impostazione/ritorno un array:modo preferito di impostazione/array ritorno

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 

Sia generare bytecodes distinti ed entrambi possono essere decompilato al loro stato precedente. Dopo aver controllato i tempi di esecuzione tramite profiler (100 milioni di iterazioni, non distorto, diversi ambienti), il tempo di _1 metodo è di ca. 4/3 il tempo di _2, anche se entrambi creano un nuovo array ed entrambi impostano ogni campo su un valore dato. I tempi sono trascurabili per la maggior parte del tempo, ma questo mi infastidisce ancora: perché _1 è visibilmente più lento? Qualcuno può controllarlo/confermarlo/spiegarmelo in un modo ragionevole supportato da JVM?

+1

Le rappresentazioni di bytecode corrispondenti sono abbastanza corte da postare qui? –

+1

I tempi medi di 1000 * 1000000 chiamate sono gli stessi per entrambi i metodi, almeno per me. – IchBinKeinBaum

+0

caro downvoter, potresti spiegare come questa domanda * non * mostri uno sforzo di ricerca e in che modo * non sia utile * ad altri utenti, specialmente se i voti vengono mostrati diversamente? – vaxquis

risposta

6

Ecco la differenza tra il bytecode (solo per i primi due elementi). Primo metodo:

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 

Secondo metodo:

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 

Come si può vedere l'unica differenza è che il riferimento matrice viene mantenuto sulla pila operando nel primo scenario (è per questo che dup appare tante volte - per evitare di perdere un riferimento a un array dopo fastore) mentre nel secondo scenario il riferimento dell'array viene mantenuto nello stack normale (dove vengono mantenuti gli argomenti del metodo e le variabili locali). In questo scenario il riferimento deve essere letto sempre (aload_1) perché fastore richiede che l'arrayref sia attivo sulla pila degli operandi.

Non dobbiamo fare ipotesi sulla base di questa bytecode - dopo tutto si è tradotto in istruzioni della CPU da e molto probabilmente in entrambi i casi di riferimento di matrice è memorizzato in uno dei registri della CPU. Altrimenti la differenza di prestazioni sarebbe enorme.

Se è possibile misurare la differenza e si stanno facendo ottimizzazioni di basso livello, selezionare la versione più veloce. Ma dubito che la differenza sia "portatile" (a seconda dell'architettura e della versione/implementazione di JVM si osserverà un diverso comportamento temporale). Detto questo, vorrei andare per la versione più leggibile, piuttosto che quella che sembra essere più veloce sul tuo computer.

+1

analisi impressionante! – AlexR

+0

Questo è esattamente quello che stavo cercando. In effetti, la differenza sembra esistere solo nell'uso di operandi/stack normali - e sono d'accordo che è troppo basso per preoccuparsi la maggior parte del tempo, mi chiedevo solo il motivo. Anch'io preferisco il codice leggibile rispetto al codice "ottimizzato". – vaxquis