2013-03-17 6 views
6

Utilizzando JProfiler, ho identificato un punto caldo nel mio codice Java a cui non riesco a dare un senso. JProfiler spiega che questo metodo richiede in media 150 μ s (674 μ s senza riscaldamento), senza contare il tempo necessario per chiamare i metodi discendenti. 150 μ s potrebbe non sembrare molto, ma in questa applicazione si aggiunge (ed è sperimentato dai miei utenti) e sembra anche molto, rispetto ad altri metodi che mi sembrano più complessi di questo. Quindi è importante per me.Il metodo Java richiede parecchio tempo che non riesco a giustificare

private boolean assertReadAuthorizationForFields(Object entity, Object[] state, 
     String[] propertyNames) { 
    boolean changed = false; 
    final List<Field> fields = FieldUtil.getAppropriatePropertyFields(entity, propertyNames); 
    // average of 14 fields to iterate over 
    for (final Field field : fields) { 
     // manager.getAuthorization returns an enum type 
     // manager is a field referencing another component 
     if (manager.getAuthorization(READ, field).isDenied()) { 
      FieldUtil.resetField(field.getName(), state, propertyNames); 
      changed = true; 
     } 
    } 
    return changed; 
} 

Ho minimizzato questo metodo in diverse direzioni, ma non mi è mai stato di grande aiuto. Non posso sottolineare abbastanza che la durata riportata da JProfiler (150 μ s) riguarda solo il codice in questo metodo e non include il tempo necessario per eseguire getAuthorization, isDenied, resetField e così via. Questo è anche il motivo per cui inizio con la pubblicazione di questo frammento, senza molto contesto, poiché il problema sembra essere con questo codice e non con le sue successive chiamate al metodo discendente.

Forse si può discutere perché – se si sente che sto vedendo fantasmi :) Comunque, grazie per il vostro tempo!

+1

Il profilo richiede un periodo di riscaldamento (per JIT). Ti stai riscaldando? JIT è disabilitato? – Java42

+0

Questo è un buon punto. Anche se lo sapevo, forse avrei lasciato cadere la palla lì. Aggiornerò il mio post, e con il tempo necessario dopo alcuni round di riscaldamento, sono diviso sull'opportunità o meno che sia eccessivo ... forse dovrei eliminare la domanda ... forse no :) –

+2

Stai usando il campionamento della CPU o la strumentazione dinamica con JProfiler? –

risposta

0

Ti suggerisco di cronometrare il metodo da te dato che il profiler non sempre fornisce un tempismo preciso.

Creare un micro-benchmark con solo questo codice e cronometrarlo per almeno 2 secondi. Per capire quanta differenza le chiamate al metodo fanno, commentale e hardcoded i valori che restituiscono.

+1

Due motivi per i tempi occasionalmente imprecisi riportati dai profiler del campionamento: 1) Se il JIT sceglie di incorporare un metodo, tale metodo non verrà mai segnalato in modo indipendente. Anche i metodi chiamati sono riportati indipendentemente (anche dopo il riscaldamento)? In caso contrario, potrebbero essere inline, nel qual caso il loro tempo verrà attribuito impropriamente ad assertReadAuthorizationForFields(). 2) Può richiedere una corsa piuttosto lunga prima che il campionamento si stabilizzi per riportare un tempo stabile. I tempi che stai vedendo da JProfiler sono stabili da 1 a corsa? – AaronD

1

comportamento Candidato che potrebbe rallentare:

  • effetto importante: Ovviamente iterazione. Se hai molti campi ... Dici 14 in media, il che è abbastanza significativo
  • Effetto principale: l'inlining dell'hotspot significherebbe che i metodi chiamati sono inclusi nei tuoi tempi - e questo potrebbe essere evidente perché le tue chiamate al metodo usano riflessione. getAppropriatePropertyFields introspects sui metadati di definizione del campo della classe; resetField richiama dinamicamente i metodi setter (possibilmente utilizzando Method.invoke() ??). Se sei alla ricerca disperata di prestazioni, puoi utilizzare una cache tramite HashSet (mappatura ElementClass-> FieldMetadataAndMethodHandle). Questo potrebbe contenere metadati del campo e MethodHandles dei metodi setter (invece di usare method.invoke, che è lento). Quindi rifletteresti solo durante l'avvio dell'applicazione e useresti il ​​veloce supporto dynamicInvoke della JVM.
  • Effetto minore - ma moltiplicato per il numero di iterazioni: se si dispone di array molto grandi per nomi di stato e proprietà, e usano campi primitivi, allora comporterebbero un certo grado di copia durante le invocazioni del metodo (parametri del metodo pass-by- ' value 'in realtà significa pass-by-reference/pass-by-copy-of-primitives)
0

Penso che il problema sia che FieldUtil sta usando Reflection e non memorizza nella cache i campi che sta usando.