C'è sempre un buon momento per utilizzare Int32, invece di sint32 in Google buffer protocollo?
-
12-09-2019 - |
Domanda
Ho letto su Google buffer protocollo di recente, che permette una varietà dei tipi di valore scalare per essere utilizzato nei messaggi.
loro documentazione , ci sono tre tipi di variabili primitivi interi -Lunghezza - int32
, uint32
e sint32
. Nella loro documentazione, si nota che int32
è "inefficiente per la codifica di numeri negativi - se il campo può avere valori negativi, utilizzare sint32
invece." Ma se si dispone di un campo che non ha i numeri negativi, suppongo che Uint32 sarebbe un tipo di meglio da usare rispetto int32
comunque (a causa della qualcosa in più ed è diminuito il costo della CPU del trattamento numeri negativi).
Così, quando sarebbe int32
essere un buon scalare da usare? È la documentazione che implica che è più efficiente solo quando raramente si ottiene numeri negativi? O è sempre preferibile utilizzare sint32
e uint32
, a seconda del contenuto del campo?
(Le stesse domande si applicano alle versioni a 64 bit di questi scalari così:. int64
, uint64
e sint64
, ma li ho lasciati fuori la descrizione del problema per l'amor di leggibilità)
Soluzione
Non ho dimestichezza con Google Protocol Buffer, ma la mia interpretazione della documentazione è:
- uso
uint32
se il valore non può essere negativo - uso
sint32
se il valore è più o meno la stessa probabilità di essere negativo come non (per qualche definizione sfocata di "la stessa probabilità di essere") - uso
int32
se il valore potrebbe essere negativo, ma questo è molto meno probabile rispetto al valore di essere positivo (ad esempio, se l'applicazione utilizza talvolta -1 per indicare un errore o un valore 'sconosciuto' e questa è una situazione relativamente raro)
Ecco quello che i documenti hanno da dire sulle codifiche ( http: / /code.google.com/apis/protocolbuffers/docs/encoding.html#types ):
v'è una differenza importante tra i tipi Acceso int (
sint32
esint64
) ei tipi int "standard" (int32
eint64
) quando si tratta di codifica numeri negativi. Se si utilizzaint32
oint64
come il tipo per un numero negativo, ilvarint
risultante è sempre dieci byte lunghi - è, in modo efficace, trattato come un grande numero intero senza segno. Se si utilizza uno dei tipi firmati, ilvarint
risultante utilizza la codifica ZigZag, che è molto più efficiente.codifica zigzag mappe interi firmato interi senza segno in modo che i numeri con un piccolo valore assoluto (ad esempio, -1) hanno un piccolo
varint
codificato valore troppo. Si fa in modo che "zig-zag" avanti e indietro attraverso i numeri interi positivi e negativi, cosicché -1 è codificato come 1, 1 è codificato come 2, -2 viene codificato come 3, e così via ...
Quindi sembra che anche se l'utilizzo dei numeri negativi è raro, a patto che la grandezza dei numeri (compresi i numeri non negativi) si sta passando nel protocollo si trova sul lato più piccola, si potrebbe essere meglio utilizzando sint32
. Se non siete sicuri, profilazione sarebbe in ordine.
Altri suggerimenti
C'è molto poco buona ragione per usare mai int * piuttosto che sint *. L'esistenza di questi tipi di più è più probabile per storico, a ritroso motivi di compatibilità, che buffer protocollo cerca di mantenere, anche attraverso le proprie versioni del protocollo.
La mia ipotesi migliore è che nella prima versione che stupidamente codificati interi negativi nella rappresentazione complemento a 2, che richiede la codifica varint massimo dimensioni di 9 byte (senza contare il tipo di byte in più). Poi sono stati bloccati con che codifica in modo da non rompere il vecchio codice e serializzazioni che già usato. Così, avevano bisogno di aggiungere un nuovo tipo di codifica, sint *, per ottenere una codifica più dimensionata in modo variabile per i numeri negativi, mentre non rompere il codice esistente. Come i progettisti non si sono resi conto questo problema dal get-go è completamente al di là di me.
La codifica varint (senza specificazione tipo, che richiede più 1 byte) può codificare un valore intero nel seguente numero di byte:
[0, 2 ^ 7): un byte
[2 ^ 7, 2 ^ 14): due byte
[2 ^ 14, 2 ^ 21): tre bytes
[2 ^ 21, 2 ^ 28): quattro byte
[2 ^ 28, 2 ^ 35): cinque byte
[2 ^ 35, 2 ^ 42): sei byte
[2 ^ 42, 2 ^ 49): sette byte
[2 ^ 49, 2 ^ 56): otto byte
[2 ^ 56, 2 ^ 64): nove byte
Se si vuole codificare simile piccola magnitudo interi negativi compatto allora si avrà bisogno di "consumare" un bit per indicare il segno. È possibile farlo attraverso un bit esplicito segno (ad un certo posizione riservata) e rappresentazione in modulo. In alternativa, si può fare la codifica a zig zag, che fa effettivamente la stessa cosa dalla sinistra spostando la grandezza di 1 bit e sottraendo 1 per i numeri negativi (in modo che il bit meno significativo indica il segno: Evens sono non negativo, le probabilità sono negativi).
In entrambi i casi, il taglio su punti in cui positivo interi richiedono più spazio ora arriva un fattore di 2 in precedenza:
[0, 2 ^ 6): un byte
[2 ^ 6, 2 ^ 13): due byte
[2 ^ 13, 2 ^ 20): tre bytes
[2 ^ 20, 2 ^ 27): quattro byte
[2 ^ 27, 2 ^ 34): cinque byte
[2 ^ 34, 2 ^ 41): sei byte
[2 ^ 41, 2 ^ 48): sette byte
[2 ^ 48, 2 ^ 55): otto byte
[2 ^ 55, 2 ^ 63): nove byte
Per fare il caso di usare int * sopra sint *, numeri negativi dovrebbero essere estremamente raro, ma possibile, e / o i valori positivi più comuni che ci si aspetta per codificare avrebbero dovuto cadere proprio dietro uno dei cut over punti che conduce ad una codifica più grande in sint * rispetto a int * (es - 2 ^ 6 contro 2 ^ 7 che porta a 2x dimensioni codificante)
.In sostanza, se avete intenzione di avere i numeri in cui alcuni possono essere negativi, poi con l'uso sint impostazione predefinita * piuttosto che int *. int * sarà molto raramente essere superiore e di solito non sarà nemmeno la pena extra pensiero che avete da dedicare verso giudicare se vale la pena o no IMHO.