2009-08-20 17 views
5

Quindi sto ottimizzando del codice srotolando alcuni loop (sì, so che dovrei fare affidamento sul mio compilatore per farlo, ma non sto lavorando con la mia scelta di compilatori) e Volevo farlo con un po 'di grazia in modo che, nel caso in cui le mie dimensioni dei dati dovessero cambiare a causa di alcune modifiche in futuro, il codice si degraderebbe elegantemente.Dimensioni C equivalenti per macro

Qualcosa di simile:

typedef struct { 
    uint32_t alpha; 
    uint32_t two; 
    uint32_t iii; 
} Entry; 

/*...*/ 

uint8_t * bytes = (uint8_t *) entry; 
#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
#if (sizeof(Entry) == 12) 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
#else 
# warning Using non-optimized code 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) 
    { 
     PROCESS_ENTRY(i); 
    } 
#endif 
#undef PROCESS_ENTRY 

Questo non funziona, naturalmente, dal momento sizeof non è disponibile per il pre-processore (almeno, questo è quello che sembrava this answer per indicare).

C'è una soluzione facile che posso usare per ottenere la sizeof una struttura dati da usare con una macro C, o sono solo SOL?

+0

Bene, sizeof() ** è ** una macro. Una macro integrata, almeno. – Havenard

+5

sizeof non è una macro, in qualsiasi forma o modulo –

+2

sizeof non è una macro, anche se offsetof è. sizeof è più di un operatore. –

risposta

17

Non è possibile fallo nel preprocessore, ma non è necessario. Basta generare una pianura if nella macro:

#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
if (sizeof(Entry) == 12) { 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
} else { 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) { 
     PROCESS_ENTRY(i); 
    } 
} 

sizeof è un'espressione costante, e il confronto costante contro la costante è anche costante. Qualsiasi compilatore C sane ottimizzerà il ramo che è sempre falso in fase di compilazione - la piegatura costante è una delle ottimizzazioni di base. Perdi lo #warning, però.

+0

È sempre bene guardare il problema da un'angolazione leggermente diversa. +1 e kudos! – qrdl

9

Se si utilizza autoconf o un altro sistema di configurazione di build, è possibile controllare la dimensione delle strutture di dati al momento della configurazione e scrivere le intestazioni (come #define SIZEOF_Entry 12). Naturalmente questo diventa più complicato durante la compilazione incrociata e così via, ma presumo che le tue architetture di costruzione e di destinazione siano le stesse.

Altrimenti si, sei sfortunato.

-1

Se si desidera la dimensione più piccola possibile per la struttura (o per allinearla a un limite di 4 byte o qualsiasi altra cosa), è possibile utilizzare gli attributi impacchettati o allineati.

In Visual C++, è possibile utilizzare #pragma pack, e nel GCC è possibile utilizzare __attribute __ ((al sacco)) e __attribute __ ((allineato (num-bytes))

+0

Questo non darà la dimensione. Inoltre, mentre l'imballaggio risparmia spazio, è probabile che costino tempo, ed è quello che sta cercando di guadagnare. –

+0

@David Thornley: Non darà la dimensione, ma farà ciò che * vuole * fare in questo blocco di codice. Imballando la struttura, egli saprà per certo che il padding non è nel modo, quindi la struttura * è * esattamente 12 byte (o la somma delle dimensioni di ciascun elemento), e quindi non c'è * bisogno * di usare una macro preprocessore per trovare la dimensione della struttura. PROCESS_ENTRY utilizza l'accesso a livello di byte su una struttura non personalizzata, quindi l'imballaggio della struttura rende possibile l'utilizzo di questa macro senza preoccuparsi del riempimento. –

+0

Devo aggiungere che in genere è meglio non immischiarsi e lasciare che l'ottimizzatore faccia il suo lavoro (soprattutto perché è molto meglio della maggior parte dei programmatori). –

5

Sei fuori di fortuna -. Il preprocessore non sa nemmeno quale sia una struttura, per non parlare di come risolverne le dimensioni

In un caso come questo si può semplicemente definire una costante a ciò che si conosce per conoscere la dimensione della struttura, quindi asserire staticamente che è effettivamente uguale alla dimensione usando il trucco dell'array di dimensioni negative

Inoltre si potrebbe provare solo a fare if (sizeof(Entry) == 12) e vedere se il compilatore è in grado di valutare le condizioni del ramo al momento della compilazione e rimuovere il codice morto. Non è una grande domanda.

+0

+1 per il suggerimento "vedi se ottimizzatore ottimizza". –

+0

Anche l'idea di usare un assert() piuttosto che un #warning è buona. Vuoi davvero un degrado aggraziato o vuoi essere chiaramente informato del problema in modo da poterlo risolvere? –

+0

E ci sono due motivi per cui non può essere 12. Uno è che c'è un padding inaspettato (nel qual caso probabilmente si vuole un errore, quindi è possibile utilizzare i pragma specifici del compilatore per impacchettare la struttura), e uno è che hai aggiunto un campo (nel qual caso si desidera aggiornare il #define in modo che rappresenti la nuova dimensione, e quindi anche decidere se aggiornare il ciclo srotolato per gestire la nuova dimensione). Quindi, in realtà, avrei l'affermazione al punto di definizione della struttura e * anche * l'avviso al momento dello srotolamento. –

1

Questo probabilmente non aiuterà, ma se avete la possibilità di fare questo in C++ è possibile utilizzare un modello per causare il compilatore che vengano avviati al ciclo appropriata al momento della compilazione:

template <std::size_t SizeOfEntry> 
void process_entry_loop(...) 
{ 
    // ... the nonoptimized version of the loop 
} 

template <> 
void process_entry_loop<12>(...) 
{ 
    // ... the optimized version of the loop 
} 

// ... 

process_entry_loop<sizeof(Entry)>(...); 
+2

Buona idea, ma sarebbe abbastanza più chiaro per rendere sizeof (Entry) il parametro del modello, e specializzarsi per 12. –

+0

+1 per il suggerimento - Mi piace! – fbrereto

1

Vengono in mente altri due approcci: scrivere una piccola app per scrivere il ciclo srotolato o utilizzare una variazione su Duff's device con le dimensioni previste della struttura.

+1

Qualsiasi compilatore moderno che generi codice più lento del dispositivo di Duff non è un ottimo compilatore. –

+0

Non dipende da quanto sia paranoico la dimensione del codice? I compilatori non necessariamente moltiplicano le dimensioni del tuo codice di 12 solo per divertimento. Senza l'input del profiler, l'ottimizzatore non ha modo di sapere quali loop valgono la pena di pagare le dimensioni del codice per guadagnare velocità. Srotolare tutto risulta in un codice di grandi dimensioni, molti errori di icache e rallentamenti. Tu, d'altra parte, puoi selezionare in modo intelligente i loop da srotolare (o, ancora più intelligentemente, usare un compilatore che può ottimizzare usando i dati del profiler). A meno che tu non intenda che il Dispositivo di Duff è ormai obsoleto a causa di trucchi nuovi e migliori di cui non sono a conoscenza. –