Domanda

La maggior parte del codice che abbia mai letto utilizza a int per la gestione degli errori standard (valori restituiti da funzioni e simili).Ma mi chiedo se si possa trarre qualche vantaggio dall'utilizzo di a uint_8 sarà un compilatore -- leggi:la maggior parte dei compilatori C sulla maggior parte delle architetture - produce istruzioni utilizzando la modalità indirizzo immediato - ovvero incorpora l'intero da 1 byte nell'istruzione ?L'istruzione chiave a cui sto pensando è il confronto dopo che una funzione, utilizzando uint_8 come tipo restituito, restituisce.

Potrei pensare alle cose in modo errato, poiché l'introduzione di un tipo da 1 byte causa solo problemi di allineamento - probabilmente c'è una ragione perfettamente sensata per cui le compilazioni amano comprimere le cose in 4 byte e questo è forse il motivo per cui tutti usano solo int - e poiché si tratta di un problema relativo allo stack piuttosto che all'heap, non vi è alcun sovraccarico reale.

Fare la cosa giusta è ciò a cui sto pensando.Ma diciamo, per amor di discussione, che si tratta di un popolare microprocessore economico per un orologio intelligente e che è configurato con 1k di memoria ma ha diverse modalità di indirizzamento nel suo set di istruzioni: D

Un'altra domanda per specializzare leggermente la discussione (x86) sarebbe:è il letterale in:

uint_32 x=func(); x==1;

E

uint_8 x=func(); x==1;

dello stesso tipo?oppure il compilatore genererà un valore letterale di 8 byte nel secondo caso.Se è così, può usarlo per generare un'istruzione di confronto che ha il letterale come valore immediato e l'int restituito come riferimento al registro. Vedere Tipi di istruzioni CMP..

Un altro riferimento per il set di istruzioni x86.

È stato utile?

Soluzione

Ecco cosa farà un particolare compilatore per il seguente codice:

extern int foo(void) ;
void bar(void)
{
        if(foo() == 31) { //error code 31
                do_something();
        } else {
                do_somehing_else();
        }
}

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   e8 fc ff ff ff          call   7 <bar+0x7>
   b:   83 f8 1f                cmp    $0x1f,%eax
   e:   74 08                   je     18 <bar+0x18>
  10:   c9                      leave
  11:   e9 fc ff ff ff          jmp    12 <bar+0x12>
  16:   89 f6                   mov    %esi,%esi
  18:   c9                      leave
  19:   e9 fc ff ff ff          jmp    1a <bar+0x1a>

un'istruzione da 3 byte per cmp.Se foo () restituisce un carattere, otteniamo b:3c 1f cmp $0x1f,%al

Se cerchi l'efficienza però.Non dare per scontato che il confronto delle cose in %a1 sia più veloce del confronto con %eax

Altri suggerimenti

Potrebbero esserci differenze di velocità molto piccole tra i diversi tipi di integrali su una particolare architettura.Ma non puoi fare affidamento su di esso, potrebbe cambiare se passi a un hardware diverso e potrebbe anche funzionare più lentamente se esegui l'aggiornamento a un hardware più recente.

E se parli di x86 nell'esempio che stai fornendo, fai un falso presupposto:Un bisogno immediato deve essere del tipo uint8_t.

In realtà gli immediati a 8 bit incorporati nell'istruzione sono di tipo int8_t e può essere utilizzato con byte, parole, dwords e qwords, in notazione C: char, short, int E long long.

Quindi su questa architettura non ci sarebbe alcun vantaggio, né nella dimensione del codice né nella velocità di esecuzione.

Dovresti utilizzare i tipi int o unsigned int per i tuoi calcoli.Utilizzo di tipi più piccoli solo per i composti (strutture/array).La ragione di ciò è che int è normalmente definito come il tipo integrale "più naturale" per il processore, tutti gli altri tipi derivati ​​potrebbero richiedere l'elaborazione per funzionare correttamente.Nel nostro progetto compilato con gcc su Solaris per SPARC il caso che accede a variabili a 8 e 16 bit aggiunge un'istruzione al codice.Quando si caricava un tipo più piccolo dalla memoria era necessario assicurarsi che la parte superiore del registro fosse impostata correttamente (estensione del segno per il tipo con segno o 0 per senza segno).Ciò ha reso il codice più lungo e ha aumentato la pressione sui registri, deteriorando le altre ottimizzazioni.

Faccio un esempio concreto:

Ho dichiarato due variabili di una struttura come uint8_t e ho ottenuto quel codice in Sparc Asm:

    if(p->BQ > p->AQ)

è stato tradotto in

ldub    [%l1+165], %o5  ! <variable>.BQ,
ldub    [%l1+166], %g5  ! <variable>.AQ,
and     %o5, 0xff, %g4  ! <variable>.BQ, <variable>.BQ
and     %g5, 0xff, %l0  ! <variable>.AQ, <variable>.AQ
cmp     %g4, %l0    ! <variable>.BQ, <variable>.AQ
bleu,a,pt %icc, .LL586  !

Ed ecco cosa ho ottenuto quando ho dichiarato le due variabili come uint_t

lduw    [%l1+168], %g1  ! <variable>.BQ,
lduw    [%l1+172], %g4  ! <variable>.AQ,
cmp     %g1, %g4    ! <variable>.BQ, <variable>.AQ
bleu,a,pt %icc, .LL587  !

Due operazioni aritmetiche in meno e 2 registri in più per altre cose

Ai processori in genere piace lavorare con le dimensioni del registro naturale, che in C è "int".

Anche se ci sono delle eccezioni, stai pensando troppo a un problema che non esiste.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top