Newsgroup: it.hobby.elettronica.digitale
Titolo: Union e parsing

Ciao,
sappiamo che non è cosa buona e giusta utilizzare le union per scrivere 
un dato e leggerne un altro. Però vedo che viene spesso fatta. A parte 
il discorso portabilità che potrebbe non interessare, in quali altri 
problemi si può incorrere?
Ad esempio, tratto da un codice opensource:
union long_union {
	int32_t dword;
	uint8_t	byte[4];
} longUnion;
long GPS_IMU_Class::join_4_bytes(unsigned char Buffer[])
{
	longUnion.byte[0] = *Buffer;
	longUnion.byte[1] = *(Buffer+1);
	longUnion.byte[2] = *(Buffer+2);
	longUnion.byte[3] = *(Buffer+3);
	return(longUnion.dword);
}
Immagino che lo si faccia per velocizzare l'assegnazione del dato, 
altrimenti occorrerebbero molti shift.
Particolari controindicazioni?
Marco

 problemi si può incorrere?
Nessun problema tecnico, ma se non sai quello che stai facendo e dove lo 
stai facendo il rischio di botti è notevole. A seconda della piattaforma 
dove stai girando (endianess, padding, dimensione dei tipi, fasi lunari, 
malocchio, ecc) l'accesso al singolo byte con lo stesso identico pezzo di 
codice non garantisce risultati uguali, infatti è proprio uno dei trucchi 
che si usano per scoprire a runtime dove si sta girando.
http://c-faq.com/misc/endiantest.html
Nessun problema quindi, basta tenere gli occhi aperti.
D'altra parte il C è roba per veri uomini; per chi ha paura c'è sempre 
Java:)

 
http://c-faq.com/misc/endiantest.html
Ecco. Però un corollario della mia domanda potrebbe essere: lo stesso 
codice sulla stessa piattaforma darà sempre lo stesso risultato? Voglio 
dire non può dipendere dal compilatore o dalle sue ottimizzazioni?
Marco

 dire non può dipendere dal compilatore o dalle sue ottimizzazioni?
A naso non dovrebbe dare nessun problema, ammesso che per esempio il
padding e l'allineamento non siano considerati una ottimizzazione. Se 
ricordo bene, su GCC si abilitavano o meno con un #pragma, ma non ricordo 
se e quanto fossero indipendenti da eventuali ottimizzazioni; vado a 
memoria ed è meglio se verifichi.

 memoria ed è meglio se verifichi.
Ok grazie.
Marco


In realtà dipende da quali passi di ottimizzazione sono abilitati.
Il compilatore di default dà per scontato che se scrivi in una 
variabile, questa scrittura non influenza le altre variabili (=non c'è 
aliasing fra tipi diversi).
I campi della union per il compilatore sono distinti e potrebbe capitare 
che, avendo cachato in qualche registro il dato di dword, venga 
ritornata la copia cachata invece che effettuare una nuova lettura dalla 
memoria e ritornare quella.
Nella funzione indicata da Marco non dovrebbero esserci problemi, perché 
il primo accesso a dword è anche l'ultimo e quindi deve essere fatto per 
forza, in ogni caso per essere sicuri che tutto funzioni, sul GCC è bene 
disabilitare le ottimizzazioni che riguardano lo strict aliasing 
(-fno-strict-aliasing).
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org


Io direi che lo si fa perché il codice è più leggibile e soprattutto per 
pigrizia :-)
A livello di velocità di esecuzione, in un caso del genere non sono 
sicuro che il codice generato sia davvero più veloce, ma in ogni caso le 
differenze non sarebbero certo significative per le performance.
In altre parole, se ti ritrovi a dover ottimizzar quella funzione, penso 
che ci siano problemi gravi :-P


 differenze non sarebbero certo significative per le performance.
Vado a vedermi l'asm generato allora. Ero convinto che con la union 
andasse a copiare i byte giusti senza fare gli 8+16+24 shift.
Marco

 (-fno-strict-aliasing).
scribb... scribb...
Grazie Francesco!
Marco

 andasse a copiare i byte giusti senza fare gli 8+16+24 shift.
Non fa lo shift del dato, ma fa comunque una operazione 
sull'indirizzamento, a seconda di dove sta quello che vuoi leggere.
Quale delle due è più veloce? Boh, a naso direi lo shift, in particolare 
se il dato è ad un indirizzo non pari, ma è una cosa che andrebbe 
verificata. In ogni caso la differenza non dovrebbe essere apprezzabile.
Le union usate per questo motivo non convengono più di tanto, a parte 
forse per una maggior semplicità di lettura del codice. A me è capitato 
di usarle pesantemente in passato per creare un buffer scrivibile e 
leggibile da più thread all'interno di macchine con diversa endianess 
(PPC<->x86) che però poi si scambiavano i buffer in rete, perciò 
bisognava stare attenti. Oltretutto, da una parte c'era un orrendo 
compilatore C IBM e dall'altra quella meravigliosa cosa che era il
Pascal Borland sotto Windows (e te lo dice un linuxaro).
Nessun problema, e i vantaggi in velocità rispetto alle soluzioni 
ortodosse con il parsing di campi testo o xml erano ben evidenti, ma era 
d'obbligo una certosina pianificazione, fare le pulci ai compilatori e ai 
vari tipi, prendere il caffè giusto alle 11 e naturalmente ricordarsi 
delle varie funzioni di conversione fra formato network e system.
Bei tempi... fosse ancora così, l'informatica, chiuderei subito
il mio giro di squillo per tornarci al volo:)

 } longUnion;
Se usi una sola architettura e una sola versione di un solo compilatore,
non c'č problema. In tutti gli altri casi non puoi fidarti. Tanto per darti
un'idea, le norme MISRA vietano del tutto l'utilizzo delle union (rule 109,
"Overlapping variable storage shall not be used").
-- 
emboliaschizoide.splinder.com

 "Overlapping variable storage shall not be used").
Ok, capito che non guadagno cosě tanto in termini di colpi di clock ne 
faccio volentieri a meno!
Marco

 memoria e ritornare quella.
Ma non c'era la keyword "volatile" appunto per evitare ciň?

 Ma non c'era la keyword "volatile" appunto per evitare ciò?
"volatile" dice al compilatore che il dato può cambiare fuori dal suo 
controllo.
Quindi non sarebbe propriamente questo caso, perché invece ha tutte le 
informazioni per fare la cosa giusta.
Il concetto di "giusto" però dipende da come è configurato.
Praticamente, se lo metti volatile di sicuro funziona, ma poi *tutti* 
gli accessi saranno effettuati davvero, anche quando non sarebbe necessario.
Disattivando lo stric-aliasing invece gli dici di fare quello che vuole 
Marco, mantenendo la possibilità di ottimizzare quando possibile.
-- 
  _|/ Francesco Sacchi - Develer S.r.l.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org/