Ambitna optymalizacja switcha w C++

0

Witam,
Zastanawiam się nad pewną sprawą. Załóżmy że mamy funkcję:

entryPoint(unsigned x)
{
switch(x)
{
case 0: cośtam; break;
....
case 80: cośtam; break;
default: break;
}
}

Czas wykonania kodu dla x=0 jest dużo mniejszy niż dla x=80, bo zanim procesor dotrze do 80-tki to trochę czasu minie na sprawdzenie 80 if-ów. Wobec tego czy da się jakoś zastosować tutaj makro albo inny zabieg by czasy startu wykonywania właściwego kodu dla wszystkich potencjalnych x-sów były takie same? Wiem, że najprościej stworzyć 80 funkcji entryPoint1-80 i po problemie, ale w moim przypadku chciałbym by była jedna funkcja entryPoint z argumentem x i pytanie czy da radę jej ciało jakoś zmienić, zastąpić switcha jakimś makrem???

0

switch zwykle zostanie zrealizowany jako skok przez indeks w tabeli (nawet z pustymi elementami), więc i tak niczego nie odczujesz, albo jako seria sub-ów

mov eax,dwCase
jmp dword ptr[lpdwTabelaFunkcji + eax *4]
mov eax,dwCase
cmp eax,0
je case_0
sub eax,80
je case_80

z hll-owego punktu widzenia nie masz wielkiego wpływu na optymalizację switach-a, możesz jedynie indeksy trzymać w kolejnosci 0,1,2,3,4,5,6 wtedy nie zachodzi potrzeba normalizacji indeksu przy skoku przez tabelę wskaźników funkcji, bo kolejne indeksy sprawiają, że wskaźniki funkcji będą leżały obok siebie

mov eax,dwCase
jmp dword ptr[lpdwTabelaFunkcji + eax *4]


lpdwTabelaFunkcji dd offset lpKodDlaCase0
                         dd offset lpKodDlaCase1
                         dd offset lpKodDlaCase2
etc.
0

OK. Nie wiedziałem tego. A czy switch jest szybszy od if-ów?
Prób odczuwania/nieodczuwania dla mnie to kilka mikrosekund przy procesorze 500MHz. Ten switch jest w Interrupt handlerze a 'x' to tak naprawdę numer przerwania. No i właśnie chodzi o to by przerwanie 48 było obsługiwane w takim samym czasie jak 0. Jeszcze znalazłem, że mogę użyć czegoś takiego:

#define irq_call(y) funkcja ## x()
void funkcja0()
{
kod dla x=0
}
...
void funkcja80()
{
kod dla x=80
}

void entryPoint(unsigned x)
{
irq_call(x); /dzięki temu teoretycznie mamy tutaj 'funkcjax()' , więc wywoła się kod każdej z funkcji w tym samym czasie/
}

0

Wszystko zależy od opcji kompilatora (i jego jakości), ja pokazałem kod na x86, switch wygenerowany jako skok przez tabelę wskaźników funkcji jest najszybszą opcją, seria if-ów nie jest kompilowana jako skok przez tabelę, przynajmniej w tych kompilatorach jakie widziałem, tu masz fajny artykuł trakujący o tym i chyba jest to to, czego właśnie szukasz

Jump Tables via Function Pointer Arrays in C/C++ - http://www.netrino.com/node/137

0

Zawsze możesz sobie zrobić ręcznie kod, który używa tablicy wskaźników do funkcji i porównać wydajność.

0

Dzięki, znalazłem. Najlepiej użyć wskaźnika do tablicy funkcji. Wtedy w entryPoint(x) mam wywołanie tablice[x] i czas dostępu do każdego kodu jest taki sam.

1 użytkowników online, w tym zalogowanych: 0, gości: 1