Perché passare è più veloce di se

Ho trovato molti libri in java che dicono che l’istruzione switch è più veloce di un’altra istruzione. Ma non ho trovato Antwhere che dicesse perché cambiare è più veloce di se .

Esempio

Ho una situazione che devo scegliere qualsiasi elemento su due che posso utilizzare uno dei seguenti modi

switch(item){ case BREAD: //eat Bread break; default: //leave the restaurant } 

o usando se dichiarazione come la seguente

 if(item== BREAD){ //eat Bread }else{ //leave the restaurant } 

considerando l’articolo e BREAD è un valore int costante

Nell’esempio sopra che è più veloce in azione e perché?

Perché ci sono bytecode speciali che consentono una valutazione efficiente delle istruzioni degli switch quando ci sono molti casi.

Se implementato con istruzioni IF, avresti un controllo, un salto alla clausola successiva, un controllo, un salto alla clausola successiva e così via. Con switch la JVM carica il valore per confrontare e itera attraverso la tabella dei valori per trovare una corrispondenza, che è più veloce nella maggior parte dei casi.

Un’istruzione switch non è sempre più veloce di un’istruzione if . È in grado di scalare meglio di un lungo elenco di istruzioni if-else quanto switch può eseguire una ricerca in base a tutti i valori. Tuttavia, per una condizione breve non sarà più veloce e potrebbe essere più lento.

L’attuale JVM ha due tipi di codici byte di commutazione: LookupSwitch e TableSwitch.

Ogni caso in un’istruzione switch ha un offset intero, se questi offset sono contigui (o per lo più contigui senza spazi vuoti grandi) (caso 0: caso 1: caso 2, ecc.), Quindi viene utilizzato TableSwitch.

Se gli offset sono distribuiti con spazi vuoti (caso 0: caso 400: caso 93748 :, ecc.), Viene utilizzato LookupSwitch.

La differenza, in breve, è che TableSwitch viene eseguito in tempo costante poiché a ogni valore compreso nell’intervallo di valori possibili viene assegnato uno specifico offset di byte-code. Pertanto, quando si assegna all’estrazione un valore di 3, si sa di saltare in avanti 3 per trovare il ramo corretto.

L’opzione di ricerca utilizza una ricerca binaria per trovare il ramo di codice corretto. Questo funziona nel tempo O (log n), che è comunque buono, ma non il migliore.

Per maggiori informazioni su questo, vedere qui: Differenza tra LookupSwitch e TableSwitch di JVM?

Quindi, per quanto è più veloce, usa questo approccio: se hai 3 o più casi i cui valori sono consecutivi o quasi consecutivi, usa sempre un interruttore.

Se hai 2 casi, usa una dichiarazione if.

Per qualsiasi altra situazione, lo switch è molto probabilmente più veloce, ma non è garantito, dal momento che la ricerca binaria in LookupSwitch potrebbe avere un cattivo scenario.

Inoltre, tieni presente che la JVM eseguirà le ottimizzazioni JIT in caso di istruzioni che tenteranno di inserire prima il ramo più caldo nel codice. Questo è chiamato “Branch Prediction”. Per ulteriori informazioni su questo, vedere qui: https://dzone.com/articles/branch-prediction-in-java

Le tue esperienze possono variare. Non so che la JVM non esegue un’ottimizzazione simile su LookupSwitch, ma ho imparato a fidarmi delle ottimizzazioni JIT e non cercare di superare in astuzia il compilatore.

A livello bytecode, la variabile object viene caricata una sola volta nel registro del processore da un indirizzo di memoria nel file .class strutturato caricato da Runtime, e questo è in un’istruzione switch; mentre in un’istruzione if, una diversa istruzione jvm viene prodotta dal tuo DE di compilazione del codice, e ciò richiede che ciascuna variabile sia caricata nei registri sebbene la stessa variabile sia usata come nella precedente istruzione if precedente. Se si conosce la codifica in linguaggio assembly allora questo sarebbe normale; sebbene i coassiali compilati da Java non siano bytecode, o codice macchina diretto, il concetto condizionale di questo documento è ancora coerente. Bene, ho cercato di evitare una maggiore tecnicità spiegando. Spero di aver reso il concetto chiaro e demistificato. Grazie.

Se stai eseguendo una quantità folle di assegni come 100+ potresti prendere in considerazione qualche astrazione.

Hai pacchetti in entrata che vanno da id 0 a 255. Ne usi forse 150. Potresti prendere in considerazione qualcosa come il seguito invece di un cambio di 150 id.

 Packets[] packets = new Packets[150]; static { packets[0] = new Login(); packets[2] = new Logout(); packets[3] = new GetMessage(); packets[7] = new AddFriend(); packets[9] = new JoinGroupChat(); // etc... not going to finish. } static final byte[] INDEX_LIST = { 0, 2, 3, 7, 9, // etc... Not going to do it, but this will convert packet id to index. }; public void handlePacket(IncomingData data) { int id = data.readByte(); packets[INDEX_LIST[id]].execute(data); } 

Dovrei anche sottolineare che la lista degli indici non è veramente necessaria e probabilmente rallenterebbe comunque il codice. Era solo un suggerimento quindi non hai posizioni vuote. Inoltre, per non parlare di questa situazione, stai perdendo solo 106 indici. Non sono sicuro al 100%, ma credo che ognuno di questi punti sia comunque nullo, quindi non ci sarebbero problemi di memoria reali.