2009-08-20 13 views
5

Więc optymalizuję niektóre kod przez rozwijanie niektórych pętli (tak, wiem, że powinienem polegać na moim kompilatorze, aby to zrobić dla mnie, ale nie pracuję z moim wyborem kompilatorów) i Chciałem zrobić to z wdziękiem, aby w przypadku zmiany rozmiaru danych z powodu pewnych zmian w przyszłości kod ulegnie eleganckiemu zniszczeniu.Równoważnik rozmiaru C dla makr

Coś jak:

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 

ten nie działa, oczywiście, ponieważ sizeof nie jest dostępne dla pre-procesor (przynajmniej, że to, co wydawało się wskazywać this answer).

Czy istnieje łatwe obejście problemu, dzięki któremu mogę uzyskać sizeof strukturę danych do użycia z makrem C, czy też po prostu SOL?

+0

Cóż, sizeof() ** to ** makro. Wbudowane przynajmniej makro. – Havenard

+5

sizeof nie jest makrem, w dowolnym kształcie lub formie –

+2

sizeof nie jest makro, chociaż offset to. sizeof jest bardziej operatorem. –

Odpowiedz

17

Nie możesz zrób to w preprocesorze, ale nie musisz tego robić. Wystarczy wygenerować zwykły if w makra:

#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 jest stałym wyrażeniem, a porównując stała przed stała jest również stała. Każdy rozsądny kompilator C optymalizuje gałąź, która zawsze jest fałszywa podczas kompilacji - stałe składanie jest jedną z najbardziej podstawowych optymalizacji. Tracisz jednak #warning.

+0

Zawsze dobrze jest spojrzeć na problem pod nieco innym kątem. +1 i kudos! – qrdl

9

Jeśli używasz autoconf lub innego systemu konfiguracji kompilacji, możesz sprawdzić rozmiar struktur danych w czasie konfiguracji i napisać nagłówki (jak #define SIZEOF_Entry 12). Oczywiście jest to bardziej skomplikowane podczas kompilacji krzyżowej i takie, ale zakładam, że twoja architektura kompilacji i docelowa są takie same.

W przeciwnym razie masz pecha.

-1

Jeśli chcesz mieć najmniejszy możliwy rozmiar struktury (lub dopasować ją do 4-bajtowej granicy lub cokolwiek innego), możesz użyć spakowanych lub wyrównanych atrybutów.

W Visual C++, można użyć #pragma pack, aw GCC można użyć __attribute __ ((pakowane)) i __ __attribute ((wyrównane (num-bajtów))

+0

To nie da rozmiaru. Co więcej, podczas pakowania zaoszczędzisz miejsce, prawdopodobnie będzie to kosztować czas i właśnie to próbuje uzyskać. –

+0

@ David Thornley: Nie poda rozmiaru, ale zrobi to, co * chce * zrobić w tym bloku kodu. Pakując strukturę, będzie wiedział na pewno, że obicie nie jest przeszkodą, więc struktura * wynosi dokładnie 12 bajtów (lub suma rozmiarów każdego elementu), a zatem * nie ma potrzeby * używania makra preprocesora aby znaleźć rozmiar struktury. PROCESS_ENTRY używa dostępu na poziomie bajta na niepodpartej strukturze, więc pakowanie struktury umożliwia korzystanie z tego makra bez obawy o dopełnienie. –

+0

Powinienem dodać, że na ogół lepiej nie mieszać się i po prostu pozwolić, aby optymalizator wykonał swoją pracę (zwłaszcza, że ​​jest o wiele lepszy od większości programistów). –

5

Jesteś pecha. - preprocesor nie wie nawet, czym jest struktura, nie mówiąc już o jakimkolwiek sposobie jej obliczenia.

W takim przypadku wystarczy # zdefiniować stałą wartość, aby poznać rozmiar struktury, a następnie statycznie potwierdzić, że jest on faktycznie równy rozmiarowi przy użyciu sztuczki o wielkości ujemnej.

Możesz również wypróbować po prostu if (sizeof(Entry) == 12) i zobacz, czy twój kompilator jest w stanie ocenić stan gałęzi podczas kompilacji i usunąć martwy kod. To nie jest takie wielkie pytanie.

+0

+1 dla podpowiedzi "Sprawdź, czy optymalizator optymalizuje". –

+0

Pomysł użycia assert() zamiast #warning jest również dobry. Czy naprawdę chcesz się zgrabnie poniżać, czy chcesz być wyraźnie poinformowany o problemie, aby móc go naprawić? –

+0

I są dwa powody, dla których nie może to być 12. Jedną z nich jest nieoczekiwane dopełnienie (w takim przypadku prawdopodobnie chcesz błędu, więc możesz użyć kompromisów specyficznych dla kompilatora, aby spakować strukturę), a jednym jest to, że masz dodano pole (w takim przypadku chcesz zaktualizować #define, aby reprezentował nowy rozmiar, a następnie zdecydować, czy zaktualizować rozwiniętą pętlę, aby obsłużyć nowy rozmiar). Tak więc, będę miał twierdzenie w punkcie definicji struktury, i * także * ostrzeżenie w momencie rozwijania. –

1

To prawdopodobnie nie pomoże, ale jeśli masz możliwość, aby to zrobić w C++ można użyć szablonu powoduje kompilator do wysyłki do odpowiedniej pętli w czasie kompilacji:

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

Dobry pomysł, ale byłoby bardziej przejrzyste, aby sizeof (Entry) parametr szablonu i specjalizuje się w 12. –

+0

+1 za cynk - Podoba mi się to! – fbrereto

1

Przypominają się dwa inne podejścia - napisać małą aplikację, aby napisać rozwiniętą pętlę, lub użyć wersji Duff's device z oczekiwanym rozmiarem struktury.

+1

Każdy nowoczesny kompilator, który generuje kod wolniejszy od urządzenia Duffa, nie jest zbyt dobrym kompilatorem. –

+0

Czy to nie zależy od tego, jak paranoi chodzi o rozmiar kodu? Kompilatory niekoniecznie mnożą rozmiar twojego kodu przez 12 tylko dla zabawy. Bez danych wejściowych profilera optymalizator nie ma możliwości sprawdzenia, które pętle są warte płacenia kodu w celu zwiększenia prędkości. Rozwinięcie wszystkiego powoduje duży kod, dużo pomyłek icache i spowolnień. Z drugiej strony, możesz inteligentnie wybrać, które pętle chcesz rozwinąć (lub, jeszcze bardziej inteligentnie, użyć kompilatora, który może zoptymalizować za pomocą danych profilera). Chyba że masz na myśli, że Urządzenie Duffa jest teraz przestarzałe z powodu nowszych, lepszych sztuczek, o których nie wiem. –