Qualche suggerimento su come migliorare le prestazioni di una conversione da stringa Java a byte []?
-
06-07-2019 - |
Domanda
Ho ereditato un pezzo di codice che fa un uso intensivo di String - > conversioni byte [] e viceversa per alcuni codici di serializzazione fatti in casa. Essenzialmente gli oggetti Java sanno come convertire le loro parti costituenti in stringhe che vengono poi convertite in byte []. Detto array di byte viene quindi passato attraverso JNI nel codice C ++ che ricostituisce il byte [] in C ++ std :: stringhe e li utilizza per avviare gli oggetti C ++ che rispecchiano gli oggetti Java. C'è un po 'di più, ma questa è una visione di alto livello di come funziona questo pezzo di codice; La comunicazione funziona in questo modo in entrambe le direzioni in modo tale che C ++ - > La transizione Java è un'immagine speculare di Java - > Transizione C ++ che ho menzionato sopra.
Una parte di questo codice - l'effettiva conversione di una stringa in un byte [] - viene inaspettatamente mostrata nel profiler mentre brucia molta CPU. Certo, ci sono molti dati che vengono trasferiti ma questo è un collo di bottiglia inaspettato.
La struttura di base del codice è la seguente:
public void convertToByteArray(String convert_me, ByteArrayOutputStream stream)
{
stream.write(convert_me.getBytes());
}
C'è un po 'di più nella funzione ma non molto. La funzione sopra viene chiamata una volta per ogni oggetto String / Stringified e dopo che tutti i componenti sono stati scritti in ByteArrayOutputStream, ByteArrayOutputStream viene convertito in un byte []. Scomponendo quanto sopra in una versione più adatta al profiler estraendo la chiamata convert_me.getBytes ()
mostra che oltre il 90% del tempo in questa funzione viene impiegato nella chiamata getBytes ().
Esiste un modo per migliorare le prestazioni della chiamata getBytes () o esiste un altro modo potenzialmente più veloce per ottenere la stessa conversione?
Il numero di oggetti che vengono convertiti è piuttosto grande. Nelle sequenze di profilatura che utilizzano solo un piccolo sottoinsieme dei dati di produzione, vedo qualcosa come 10 milioni e più chiamate alla funzione di conversione sopra.
A causa del fatto che siamo molto vicini al rilascio del progetto in produzione, ci sono alcune soluzioni alternative che al momento non sono possibili:
- Riscrivi l'interfaccia di serializzazione per passare semplicemente gli oggetti String attraverso il livello JNI. Questo è il modo ovvio (per me) di migliorare la situazione, ma richiederebbe un reingegnerizzazione importante del livello di serializzazione. Dato che entreremo in UAT all'inizio di questa settimana, è troppo tardi per apportare questo tipo di cambiamento complesso. È il mio top todo per la prossima versione, quindi sarà fatto; Ho comunque bisogno di una soluzione alternativa fino ad allora, ma finora il codice funziona, è stato usato per anni e ha risolto la maggior parte dei nodi. Bene, a parte la performance.
- Anche la modifica della JVM (attualmente 1.5) non è un'opzione. Sfortunatamente questa è la JVM predefinita installata sui computer client e l'aggiornamento a 1.6 (che potrebbe o non potrebbe essere più veloce in questo caso) non è purtroppo possibile. Chiunque abbia lavorato in grandi organizzazioni probabilmente capisce perché ...
- Oltre a questo, stiamo già incontrando vincoli di memoria, quindi tentando di memorizzare nella cache almeno le stringhe più grandi e la loro rappresentazione di array di byte, pur essendo una soluzione potenzialmente elegante, è probabile che causi più problemi di quanti ne risolva
Soluzione
Suppongo che parte del problema potrebbe essere che una stringa Java è in formato UTF-16, ovvero due byte per carattere; quindi getBytes ()
sta facendo un sacco di lavoro per convertire ogni elemento UTF-16 in uno o due byte, a seconda del set di caratteri corrente.
Hai provato a utilizzare CharsetEncoder - questo dovrebbe darti un maggiore controllo sulla codifica String e consentire di saltare parte dell'overhead nell'implementazione predefinita getBytes
.
In alternativa, hai provato a specificare esplicitamente il set di caratteri su getBytes
e hai utilizzato US-ASCII come set di caratteri?
Altri suggerimenti
Vedo diverse opzioni:
Se si tratta sempre delle stesse stringhe che si convertono, è possibile memorizzare nella cache il risultato in una WeakHashMap.
Inoltre, dai un'occhiata al metodo getBytes () (il sorgente è disponibile se installi l'SDK) per vedere esattamente cosa fa.
Il problema è che tutti i metodi in Java, anche oggi, allocano la memoria con la produzione UTF-8. Per ottenere il performante di codifica dovrai scrivere codice personalizzato e riutilizzare il buffer byte []. Colfer può generare il codice o semplicemente copiarne l'implementazione.