2016-04-11 23 views
11

Ho un ciclo for che viene eseguito 4096 volte e dovrebbe essere il più veloce possibile. Le prestazioni sono davvero importanti qui. Attualmente utilizzo metodi getter all'interno del ciclo che restituiscono valori o oggetti dai campi che non cambiano mentre il ciclo è in corso.Java utilizza getter in per loop o crea una variabile locale?

Esempio:

for (;;) { 
    doSomething(example.getValue()); 
} 

C'è qualche testa usando getter? È più veloce utilizzando il seguente modo?

Esempio:

Object object = example.getValue(); 
for (;;) { 
    doSomething(object); 
} 

Se sì, è che anche per l'accesso ai campi pubblici come example.value?

Modifica: non uso System.out.println() all'interno del ciclo.

Modifica: alcuni campi non sono final. Nessun campo è volatile e nessun metodo (getter) è synchronized.

+0

Salvare l'output in una stringa locale sarebbe più veloce di un getter o di un oggetto dereferenziazione. Ma questo è misurato in "teensy" – Jamie

risposta

7

Come Rogério answered, ottenendo il riferimento all'oggetto di fuori del ciclo (Object object = example.getValue();) sarà probabilmente più veloce (o sarà, almeno non essere più lento) di chiamare il getter all'interno del ciclo perché

  • nel caso "peggiore" , example.getValue() potrebbe effettivamente fare qualcosa di molto costoso dal punto di vista computazionale sullo sfondo, nonostante che lo getter methods debba essere "banale". Assegnando un riferimento una volta e riutilizzandolo, si esegue questo calcolo costoso una sola volta.
  • nel caso "migliore", example.getValue() fa qualcosa di banale come return value; e quindi assegnarlo all'interno del ciclo non sarebbe più costoso di quello esterno al ciclo dopo il compilatore JIT inlines the code.

Tuttavia, più importante è la differenza semantica tra i due ed i suoi possibili effetti in un ambiente multi-threaded: Se lo stato dell'oggetto example cambia in modo che provoca example.getValue() restituire riferimenti a oggetti diversi, è possibile che, in ogni iterazione, il metodo doSomething(Object object) funzioni effettivamente su un'istanza diversa di Object chiamando direttamente doSomething(example.getValue());. D'altra parte, chiamando il getter fuori del ciclo e impostando un riferimento istanza restituita (Object object = example.getValue();), doSomething(object); opererà su objectn volte per n iterazioni.

Questa differenza nella semantica può causare il comportamento in un ambiente a più thread per essere radicalmente diverso da quello in un ambiente a thread singolo. Inoltre, questo non deve essere un vero problema di "multi-threading in memoria": se example.getValue() dipende ad es. database/HDD/risorse di rete, è possibile che questi dati cambino durante l'esecuzione del ciclo, rendendo possibile che venga restituito un oggetto diverso anche se l'applicazione Java stessa è a thread singolo.Per questo motivo, è meglio considerare ciò che si desidera effettivamente realizzare con il ciclo e quindi scegliere l'opzione che meglio riflette il comportamento desiderato.

0

Se è necessario eseguirlo il più velocemente possibile, non utilizzare System.out.println nelle sezioni critiche.

Riguardo al getter: C'è un leggero sovraccarico per l'uso di getter, ma non dovresti preoccupartene. Java ha l'ottimizzazione getter e setter nel compilatore JIT. Quindi alla fine verranno sostituiti dal codice nativo.

+1

Non uso 'System.out.println()' nel mio codice. Questo è solo un cattivo esempio. – stonar96

4

Dipende dal getter.

Se si tratta di un semplice getter, il JIT lo accosterà comunque a un accesso diretto sul campo, quindi non ci sarà una differenza misurabile. Dal punto di vista dello stile, usa il getter - è meno codice.

Se il getter accede a un campo volatile, è disponibile un ulteriore accesso alla memoria poiché il valore non può essere mantenuto nel registro, tuttavia l'hit è molto piccolo.

Se il getter è synchronized, quindi l'utilizzo di una variabile locale sarà misurabilmente più veloce poiché non è necessario ottenere e rilasciare i blocchi ogni chiamata, ma il codice di ciclo utilizzerà il valore potenzialmente obsoleto del campo nel momento in cui fu chiamato Getter.

+0

Grazie per la risposta. L'intero codice è single threaded. Quindi non ci sono campi volatili o metodi sincronizzati. I getter sono semplici getter. – stonar96

4

si dovrebbe preferire una variabile locale al di fuori del ciclo, per i seguenti motivi:

  1. Esso tende a rendere il codice più facile da leggere/capire, evitando il metodo annidato chiamate come doSomething(example.getValue()) in una sola riga di codice e consentendo al codice di fornire un nome migliore, più specifico, al valore restituito dal metodo getter.
  2. Non tutti i metodi getter sono banali (ad esempio, a volte fanno un lavoro potenzialmente costoso), ma gli sviluppatori spesso non se ne accorgono, assumendo che un dato metodo sia banale e poco costoso quando non lo è. In tali casi, il codice potrebbe richiedere un notevole calo delle prestazioni senza che lo sviluppatore se ne accorga. L'estrazione in una variabile locale tende ad evitare questo problema.
1

È molto facile preoccuparsi delle prestazioni molto più del necessario. Conosco la sensazioneAlcune cose da considerare:

  1. 4096 non è molto, quindi a meno che questo non debba essere completato in un tempo estremamente breve non preoccuparti troppo delle prestazioni.
  2. Se c'è qualcos'altro in remoto in questo loop, il getter non ha importanza.
  3. L'ottimizzazione prematura è la radice di tutto il male. Concentrati a rendere il tuo codice corretto e chiaro prima. Quindi misura e traccia il profilo e restringi la cosa più costosa, e prenditi cura di ciò. Migliora l'algoritmo attuale se possibile.

quanto riguarda la tua domanda, non so esattamente ciò che il JIT fa, ma a meno che non posso provare con certezza che example.getValue() o example.value non cambia nel ciclo (che è difficile da fare a meno che il campo è final e il getter è banale), quindi logicamente non c'è modo di evitare di chiamare ripetutamente il getter nel precedente esempio poiché rischierebbe di cambiare il comportamento del programma. Le chiamate ripetute sono certamente una quantità diversa di lavoro extra.

Detto questo, crea la variabile locale all'esterno del ciclo, anche se non è più veloce, perché è più chiaro. Forse questo ti sorprende, ma il buon codice non è sempre il più breve. Esprimere l'intento e altre informazioni è estremamente importante. In questo caso, la variabile locale al di fuori del ciclo rende ovvio a chiunque legga il codice che l'argomento di doSomething non cambia (specialmente se lo si rende definitivo) che è utile sapere. Altrimenti potrebbero dover fare qualche altro scavo per assicurarsi che sappiano come si comporta il programma.

+0

"* quindi non c'è logicamente modo di evitare di chiamare ripetutamente il getter *" => il metodo getter può ancora essere sottolineato, indipendentemente da ciò che sta facendo ... – assylias

+0

@assylias sì, ma è ancora un po 'di lavoro, anche se sta solo controllando il valore di un campo. –