In Go, la conversione tra string e []byte di solito comporta l'allocazione di nuova memoria, poiché le stringhe sono immutabili e gli slice di byte sono mutabili. Ci sono eccezioni per alcune ottimizzazioni interne del compilatore (analisi dell'escape), ma non sono garantite e cambiano tra le versioni di Go.
Conversione diretta:
[]byte(s string): alloca sempre un nuovo array di byte e copia i dati.string(b []byte): copia anch'essa sempre i dati dall'array di byte in una nuova stringa.Zero allocation è un approccio in cui evitiamo allocazioni inutili. Nella libreria standard di Go, esiste una conversione non sicura tramite il pacchetto unsafe, che consente di puntare agli stessi dati senza copia. Questo può essere utilizzato solo con una comprensione completa dei rischi.
import ( "reflect" "unsafe" ) func BytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func StringToBytes(s string) []byte { sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) bh := reflect.SliceHeader{ Data: sh.Data, Len: sh.Len, Cap: sh.Len, } return *(*[]byte)(unsafe.Pointer(&bh)) }
Nella maggior parte dei casi, è meglio non utilizzare queste tecniche, poiché compromettono la sicurezza dei dati (è possibile modificare il contenuto della stringa tramite lo slice condiviso, il che porta a errori).
Spesso si chiede: "È vero che durante la conversione da string a []byte non ci sarà allocazione se la stringa è piccola o costante?"
Risposta corretta: No. Indipendentemente dalle dimensioni della stringa, il compilatore creerà sempre un nuovo slice di byte e copierà il contenuto. Le eccezioni sono solo nelle ottimizzazioni unsafe, che non garantiscono sicurezza e non sono supportate dalla documentazione ufficiale di Go.
Storia
Il team stava scrivendo un servizio ad alta intensità per l'analisi dei log e continuava a convertire le stringhe in slice di byte e viceversa per l'elaborazione. In situazioni di picco, il garbage collector spendeva fino al 30% del tempo di CPU per raccogliere copie di breve durata. Dopo il profiling, si è scoperto che ogni conversione string↔[]byte allocava un'area di memoria separata. Dopo l'implementazione di pool e il redesign dell'API, è stato possibile ridurre notevolmente il numero di conversioni, abbassando il carico sul GC di metà.
Storia
Uno degli sviluppatori ha ottimizzato la gestione dei JSON utilizzando la conversione unsafe da bytes a string per evitare allocazioni. Inizialmente, l'aumento delle prestazioni era evidente, ma dopo un mese si sono verificati crash: qualche buffer byte era riutilizzato, la stringa puntava a dati vecchi e modificati. L'unica soluzione è stata tornare alle copie standard e riprogettare l'API interprocesso.
Storia
Nella trasmissione di grandi dati binari su rete hanno deciso di "ottimizzare" la serializzazione, utilizzando BytesToString (senza copia). Una volta la stringa inviata si è rivelata pubblicamente visibile e il contenuto dello slice di byte è stato immediatamente sovrascritto, portando all'invio di spazzatura e alla perdita della parte privata dei dati nei log dei pacchetti errati. Alla fine, la deduplicazione della memoria si è trasformata in una perdita di dati privati!