2015-04-25 14 views
33

Dal JDK 7 ho felicemente con il metodo ha introdotto a respingere null valori che sono passati ad un metodo che non li possono accettare:Oggetti.requireNonNull è meno efficiente del vecchio modo?

private void someMethod(SomeType pointer, SomeType anotherPointer) { 
    Objects.requireNonNull(pointer, "pointer cannot be null!"); 
    Objects.requireNonNull(anotherPointer, "anotherPointer cannot be null!"); 
    // Rest of method 
} 

Penso che questo metodo rende il codice molto ordinata che è facile da leggere, e sto cercando di incoraggiare i colleghi a usarlo. Ma uno (particolarmente competenti) collega è resistente, e dice che il vecchio modo è più efficiente:

private void someMethod(SomeType pointer, SomeType anotherPointer) { 
    if (pointer == null) { 
     throw new NullPointerException("pointer cannot be null!"); 
    } 
    if (anotherPointer == null) { 
     throw new NullPointerException("anotherPointer cannot be null!"); 
    } 
    // Rest of method 
} 

Dice che chiama requireNonNull prevede il posizionamento altro metodo nello stack di chiamate JVM e si tradurrà in prestazioni di peggio di un semplice == null controllo.

Quindi la mia domanda: c'è qualche prova di una sanzione di prestazioni sostenute utilizzando i metodi Objects.requireNonNull?

+13

No. Non lo è. Il tuo collega "informato" evidentemente non è così informato come pensa (s). Qualunque impatto sulle prestazioni che potrebbe avere è trascurabile e molto probabilmente verrà sottolineato dalla JIT se rileva che il metodo è fortemente utilizzato. –

+13

Il JIT in linea felicemente le chiamate di metodo. Dovresti ricordare la prima regola di ottimizzazione per il tuo vecchio padawan: * non *. Dovrebbe essere lui a dimostrare che questa chiamata al metodo è la causa di un problema significativo prima di obbligare tutti a produrre codice meno leggibile. –

+4

[OpenJDK bug 8073479] (https://bugs.openjdk.java.net/browse/JDK-8073479) ha convertito i tradizionali controlli nulli 'foo.getClass()' di JDK per usare 'Objects.requireNonNull', invece, non richiedendo prestazioni degrado e talvolta un miglioramento. –

risposta

38

Let alla realizzazione di requireNonNull nel JDK di Oracle:

public static <T> T requireNonNull(T obj) { 
    if (obj == null) 
     throw new NullPointerException(); 
    return obj; 
} 

Ecco, questo è molto semplice . La JVM (Oracle, comunque) include un compilatore just-in-time a due stadi di ottimizzazione per convertire bytecode in codice macchina. Inline metodi banali come questo se può ottenere prestazioni migliori in questo modo.

Quindi no, non è probabile che sia più lento, non in alcun modo significativo, non ovunque sia importante.

Quindi la mia domanda: c'è qualche evidenza di una penalizzazione delle prestazioni di essere sostenute utilizzando i metodi Objects.requireNonNull?

L'unica prova che avrebbe avuto importanza sarebbe misurazioni delle prestazioni del vostro codebase, o di codice progettato per essere altamente rappresentativo di esso. È possibile testare questo con uno strumento di test delle prestazioni decente, ma a meno che il tuo collega non indichi un esempio reale di un problema di prestazioni nella tua base di codice relativa a questo metodo (piuttosto che un benchmark sintetico), tenderei ad assumere tu e lui/lei avete pesce più grande da friggere.


Come un po 'di parte, ho notato il tuo metodo di campionamento è un metodo private. Quindi solo il codice che il tuo team sta scrivendo lo chiama direttamente. In tali situazioni, è possibile verificare se si dispone di un caso d'uso per assertions anziché per i controlli di runtime. Le asserzioni hanno il vantaggio di non essere affatto eseguite nel codice "rilasciato" e quindi di essere più veloci di entrambe le alternative nella tua domanda. Ovviamente ci sono posti in cui sono necessari controlli di runtime, ma quelli sono di solito in punti di gatekeeping, metodi pubblici e così via. Solo FWIW.

+0

Qualcos'altro che mi viene in mente: esiste un costo nella creazione di un'istanza 'String' quando si chiama la versione di' requireNonNull' che accetta un messaggio di errore come secondo parametro? In tal caso, nascondere il messaggio di errore all'interno di un blocco 'if' evita questo costo quando il test' == null' è 'false'? – Bobulous

+5

@Bobulous: la stringa esiste già, poiché i valori letterali stringa sono precaricati quando il file di classe viene caricato e inserito nel pool di stringhe. Se stavi facendo 'Objects.requireIfNull (blah," literal "+ runtimeString);', quindi dovresti creare un 'StringBuilder' in runtime (sotto le copertine) per fare la concatenazione, anche se la stringa risultante non fosse Usato. Potrebbe essere un po 'sovraccarico. Ma non se stai usando letterali o riferimenti a stringhe statiche esistenti. ** E ** non sottovalutare mai il JIT! E 'completamente permesso non solo in linea, ma * riordinare *, codice per evitare negozi morti come quello. –

+0

@Bobulous Questa risposta è perfetta, lasciatemi aggiungere che, nel caso in cui avessi bisogno di un messaggio dinamico, prendi in considerazione l'uso di Guava [checkNotNull con errorMessageTemplate] (http://docs.guava-libraries.googlecode.com/git/javadoc /com/google/common/base/Preconditions.html#checkNotNull(T,%20java.lang.String,%20java.lang.Object ...)). Ciò elimina la concatenazione di stringhe se non è necessaria (viene invece creato un array di varargs che è meno costoso ed eventualmente più facile da eliminare dal JIT). – maaartinus

1

Probabilmente il tuo collega ha torto.

JVM è molto intelligente e molto probabilmente incorpora il metodo Objects.requireNonNull(...). La performance è discutibile, ma ci saranno sicuramente delle ottimizzazioni molto più gravi di questa.

È necessario utilizzare il metodo di utilità da JDK. sguardo

5

Se si desidera prove ... quindi il modo per farlo è scrivere un micro-benchmark.

(Consiglio di osservare prima il progetto Calliper o JMH ... secondo la raccomandazione di Boris. In ogni caso, non provare e scrivere un micro-benchmark da zero. Ci sono troppi modi per sbagliare).

Tuttavia, si può dire al vostro collega due cose:

  • Il compilatore JIT fa un buon lavoro di inlining chiamate di metodo di piccole dimensioni, ed è probabile che questo accadrà in questo caso.

  • Se non fosse in linea la chiamata, è probabile che la differenza di prestazioni sarebbe solo da 3 a 5 istruzioni, ed è altamente improbabile che farebbe una differenza significativa.

+1

Questo [confronto JMH dei metodi di controllo nullo] (http://cr.openjdk.java.net/~shade/scratch/NullChecks.java) suggerisce che anche nel caso in cui l'inlining fallisce il metodo 'Objects.requireNonNull' richiede meno di 2ns più lungo del codice if-null. E dove il codice è stato delineato, non sembra esserci alcuna differenza significativa tra i due modi. – Bobulous

13

Formalmente, vostro collega ha ragione:

  • Se someMethod() o traccia corrispondente non è abbastanza caldo, il bytecode viene interpretato, e stack frame in più si crea

  • Se someMethod() viene richiamato al 9 ° livello di profondità dall'hot spot, le chiamate requireNonNull() non devono essere in linea a causa di MaxInlineLevel JVM Option

  • Se il metodo non è in linea per nessuno dei motivi sopra indicati, argomento di T.J. Crowder entra in gioco se si utilizza la concatenazione per produrre il messaggio di errore

  • Anche se requireNonNull() è in linea, JVM spreca tempo e spazio per eseguire questa operazione.

D'altra parte, v'è FreqInlineSize opzione JVM, che vieta inlining troppo grande (in bytecode) metodi. I bytecode del metodo vengono conteggiati da soli, senza la dimensione contabile dei metodi, chiamati all'interno di questo metodo. Pertanto, l'estrazione di parti di codice in metodi indipendenti potrebbe essere utile a volte, nell'esempio con requireNonNull() questa estrazione è già stata effettuata per te.

-3

Objects.requireNonNull è più ottimizzato come se si fosse riusabilità del codice. Anche in oracolo requireNonNull si definito come

public static <T> T requireNonNull(T obj) { 
    if (obj == null) 
     throw new NullPointerException(); 
    return obj; 
} 

così la sua già in bytecode.

Problemi correlati