2013-09-06 15 views
5

Questo Java method si abitua nei benchmark per simulare lento calcolo:Perché questo metodo non viene ottimizzato?

static int slowItDown() { 
    int result = 0; 
    for (int i = 1; i <= 1000; i++) { 
     result += i; 
    } 
    return result; 
} 

Questo è IMHO una pessima idea, come il suo corpo può ottenere sostituito da return 500500. Questo sembra non accadere mai; probabilmente a causa di tale ottimizzazione è irrilevante per il codice reale come Jon Skeet ha dichiarato.

È interessante notare che un metodo leggermente più semplice con result += 1; viene completamente ottimizzato (caliper riporta 0,460543 ns).

Ma anche quando siamo d'accordo che l'ottimizzazione di distanza metodi ritornano un risultato costante è inutile per il codice vero e proprio, c'è ancora svolgimento del ciclo, che potrebbe portare a qualcosa di simile

static int slowItDown() { 
    int result = 0; 
    for (int i = 1; i <= 1000; i += 2) { 
     result += 2 * i + 1; 
    } 
    return result; 
} 

Quindi la mia domanda rimane: perché c'è l'ottimizzazione eseguito qui?

Contrariamente a quanto ho scritto in origine; Devo aver visto qualcosa che non c'era.

+1

Come hai fatto a testare questo? Se utilizzi il JIT, probabilmente osserverai cose simili cambiando leggermente il codice, poiché sono coinvolte molte euristiche. Non è assolutamente garantito che il JIT applichi anche le ottimizzazioni più semplici come l'inlining, poiché lo fa solo una volta ritenuto necessario. –

+0

È questo codice reale? Se sai cosa significa tornare, perché non scrivere il codice in questo modo? Sono contento che i compilatori JIT sono sintonizzati per ottimizzare * codice reale * piuttosto che ottimizzare il codice di distanza che non si verificherebbe nella realtà. (Gli ottimizzatori statici hanno un margine di manovra un po 'maggiore, ma ricorda che ogni ottimizzazione che un compilatore JIT cerca di trovare ha un costo in fase di esecuzione *.) –

+0

@JonSkeet: osservo, imbarazzantemente, che il collegamento indirizza al codice di benchmarking di Guava, che Normalmente spero di fidarmi ... –

risposta

3

Bene, la JVM fa ottimizza tale codice. La domanda è quante volte deve essere rilevato come un hotspot reale (i benchmark fanno qualcosa in più rispetto a questo singolo metodo, di solito) prima che venga analizzato in questo modo. Nel mio setup richiedeva 16830 invocazioni prima che il tempo di esecuzione fosse (quasi) zero.

È corretto che tale codice non compaia nel codice reale. Tuttavia potrebbe rimanere dopo diverse operazioni di inlining di altri hotspot che trattano i valori non essendo costanti in fase di compilazione ma costanti di runtime o costanti di fatto (valori che potrebbero cambiare in teoria ma non in pratica). Quando un pezzo di codice rimane, è un grande vantaggio ottimizzarlo completamente ma non è previsto che succeda presto, cioè quando si chiama correttamente dal metodo principale.

Aggiornamento: ho semplificato il codice e l'ottimizzazione è arrivata anche prima.

public static void main(String[] args) { 
    final int inner=10; 
    final float innerFrac=1f/inner; 
    int count=0; 
    for(int j=0; j<Integer.MAX_VALUE; j++) { 
    long t0=System.nanoTime(); 
    for(int i=0; i<inner; i++) slowItDown(); 
    long t1=System.nanoTime(); 
    count+=inner; 
    final float dt = (t1-t0)*innerFrac; 
    System.out.printf("execution time: %.0f ns%n", dt); 
    if(dt<10) break; 
    } 
    System.out.println("after "+count+" invocations"); 
    System.out.println(System.getProperty("java.version")); 
    System.out.println(System.getProperty("java.vm.version")); 
} 
static int slowItDown() { 
    int result = 0; 
    for (int i = 1; i <= 1000; i++) { 
     result += i; 
    } 
    return result; 
} 

...

execution time: 0 ns 
after 15300 invocations 
1.7.0_13 
23.7-b01 

(64Bit Server VM)

+0

Potresti cortesemente postare il tuo codice? Sono curioso di sapere come tutto sia stato ottimizzato - ho scritto che pensavo fosse successo anche a me, ma ora penso di vedere qualcosa che non c'era. – maaartinus

+0

Sembra che il metodo venga ottimizzato * via * anziché ottimizzato (cioè piegato a una costante). Ho provato qualcosa come 'x + = slowItDown()' e stampato 'x' alla fine e il tempo non è mai sceso sotto i 300 ns. Questo corrisponde a ciò che hai scritto; è solo che ero più curioso del folding (in quanto ciò avrebbe distrutto il punto di riferimento dal quale ho preso il metodo). – maaartinus

Problemi correlati