2010-07-21 13 views
9

Stavo usando newInstance() in un'area del mio codice di importanza critica per le prestazioni. La firma del metodo è:Prestazioni di nuovo operatore rispetto a newInstance() in Java

<T extends SomethingElse> T create(Class<T> clasz)

mi passano Something.class come argomento, ottengo un'istanza di SomethingElse, creata con newInstance().

Oggi sono tornato per cancellare questa prestazione TODO dall'elenco, quindi ho eseguito un paio di test dell'operatore new rispetto a newInstance(). Sono rimasto molto sorpreso dalla penalizzazione delle prestazioni di newInstance().

ho scritto un po 'su di esso, qui: http://biasedbit.com/new-vs-newinstance/

(. Ci scusiamo per la promozione di auto ... mi piacerebbe inserire il testo qui, ma a questa domanda sarebbe cresciuto di proporzioni)

Cosa Mi piacerebbe sapere perché il flag -server fornisce un tale aumento di prestazioni quando il numero di oggetti creati cresce in gran parte e non per valori "bassi", ad esempio 100 o 1000.

Ho imparato la lezione con tutta la cosa dei riflessi, questa è solo una curiosità per le ottimizzazioni che la JVM esegue in runtime, in particolare con il flag -server. Inoltre, se sto facendo qualcosa di sbagliato nel test, apprezzerei il tuo feedback!


Modifica: ho aggiunto una fase di riscaldamento e ora i risultati sono più stabili. Grazie per l'input!

+0

Il collegamento è interrotto:/ –

+0

L'ho risolto! – biasedbit

+0

Il post del blog ha [spostato di nuovo] (http://biasedbit.com/blog/new-vs-newinstance). –

risposta

5

Ho imparato la lezione con l'intera cosa dei riflessi, questa è solo una curiosità sulle ottimizzazioni eseguite da JVM in runtime, specialmente con il flag -server. Inoltre, se sto facendo qualcosa di sbagliato nel test, apprezzerei il tuo feedback!

Rispondendo per prima alla seconda parte, il codice sembra commettere l'errore classico per i micro-benchmark Java e non "riscaldare" la JVM prima di effettuare le misurazioni. L'applicazione deve eseguire il metodo che esegue il test alcune volte, ignorando le prime iterazioni ... almeno finché i numeri non si stabilizzano. La ragione di ciò è che una JVM deve fare molto lavoro per avviare un'applicazione; per esempio. caricamento delle classi e (quando sono state eseguite alcune volte) JIT che compila i metodi in cui viene impiegato un tempo significativo per l'applicazione.

Penso che la ragione per cui "-server" sta facendo la differenza è che (tra le altre cose) cambia le regole che determinano quando compilare JIT. Il presupposto è che per un "server" è meglio JIT prima questo dà avvio più lento ma una maggiore velocità effettiva. (Al contrario, un "client" è sintonizzato per posticipare la compilazione JIT in modo che l'utente ottenga prima una GUI funzionante.)

+0

Ho aggiunto un paio di iterazioni di riscaldamento prima di eseguire i test effettivi e sto ottenendo cifre migliori e più stabili. Con flag -server, newInstance() si stabilizza a 2,5 ~ 2,9 volte più lentamente del nuovo (sia per 100 che per 1kk oggetti). Senza il -server va ancora fino a 8 ~ 9 volte più lento per 100 oggetti fino a 25 più lenti per 1kk). Ottimo consiglio! :) – biasedbit

+0

Un modo migliore per scaldare correttamente la JVM, è passare gli argomenti della riga di comando -XX: + PrintCompilation che renderanno la JVM stampare tutte le compilation JIT e le ottimizzazioni che fa come le esegue. Quindi assicurati che nessuno dei passaggi della compilazione avvenga durante la tua esecuzione a tempo. – LordOfThePigs

+0

@LordOfThePigs - Non sono d'accordo. In generale, il modo migliore consiste nell'eseguire un gran numero di iterazioni (alternate), registrare i tempi e scartare quelli iniziali che sono anomali. Quindi media. Questo si prende cura di altre fonti di problemi e di JIT. L'approccio '-XX: + PrintCompilation' affronta solo le anomalie JIT. –

1

Tra le altre cose, il profilo di garbage collection per l'opzione -server ha valori predefiniti survivor space sizing significativamente diversi.

A una lettura più ravvicinata, vedo che il tuo esempio è un micro-benchmark ei risultati potrebbero essere contro-intuitivi. Ad esempio, sulla mia piattaforma, le chiamate ripetute a newInstance() vengono efficacemente ottimizzate durante le ripetute corse, rendendo newInstance() visualizzato 12,5 volte più veloce di new.

+0

Ho eseguito con -XX: + PrintGCDetails e ho visto solo 1 raccolta: la raccolta completa che ho forzato tra i due test. Mi sono assicurato di fornire abbastanza memoria per l'app in modo che non avrebbe bisogno di ridimensionare o raccogliere (in realtà stavo anche impedendo il ridimensionamento usando lo stesso -Xmx come -Xms). In queste condizioni, credo che GC non sia stato un fattore decisivo nei risultati. – biasedbit

+0

@brunodecarvalho: Sì, capisco cosa intendi, e ho elaborato sopra. Potrebbe essere necessario riscrivere il benchmark per ottenere risultati significativi. – trashgod

+0

Sì, avevo paura dell'effetto micro-benchmark qui. Sto solo aggiungendo la nuova istanza a quell'array di oggetti nella speranza che il compilatore non si accorga che è un'operazione inutile e cancella completamente l'intero ciclo. Lo proverò di nuovo usando il sistema che stavo per ottimizzare. Tuttavia, trovo un po 'strano che newInstance() diventi più veloce del nuovo. Guardando la fonte di JDK, sembra che ci sia un sacco di overhead che newInstance() attraversa e che non è nuovo. – biasedbit

1

IMHO la penalizzazione delle prestazioni deriva dal meccanismo di caricamento della classe. In caso di riflessione vengono utilizzati tutti i meccanismi di sicurezza e quindi la penalità di creazione è maggiore. In caso di nuovo operatore le classi sono già caricate nella VM (verificate e preparate dal classloader predefinito) e l'istanziazione effettiva è un processo economico. Il parametro -server esegue molte ottimizzazioni JIT per il codice utilizzato di frequente. Potresti volerlo provare anche con il parametro -batch che cambierà il tempo di avvio ma poi il codice verrà eseguito più velocemente.

+0

La tua spiegazione ha senso, poiché ho esaminato il codice e ho calcolato che l'intero meccanismo di sicurezza avrebbe aggiunto un sovraccarico significativo. Tuttavia, il flag -batch non sembra essere valido. – biasedbit

+0

@brunodecarvalho: hai ragione! La sintassi è -Xbatch non batch come ho digitato. Per i dettagli vedere: http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/tooldocs/windows/java.html e http : //download.oracle.com/docs/cd/E17409_01/javase/6/docs/technotes/tools/windows/java.html –

+0

Non sembra fare molta differenza. Ho rimosso la fase di riscaldamento per vedere se avrebbe influenzato i risultati, ma sto ottenendo praticamente gli stessi valori con -Xbatch e senza. Comunque, questa è una bandiera molto interessante! – biasedbit

Problemi correlati