Ja en nee. Een if-statement in pure ASM is niets meer dan een "cmp" (compare) instructie gevolgd door een jump instructie, bijvoorbeeld "je" (jump equal). Bij een jump wordt de "locatie register" aangepast aan het nieuwe adres, zijnde EIP, wat vrij weinig overhead heeft. Bij een switch statement wordt er gebruik gemaakt van een zogenaamde jump-tabel, wat in principe een serie addressen is (in sommige gevallen zelfs absolute addressen). Bij meer complexe switch-statements zijn er zelfs twee vertaal-tabellen, ééntje om de eigenlijke case uit te vinden, en de tweede om het adres van deze case te achterhalen. Een voorbeeld hiervan is;
.
C++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| unsigned char ucWaarde = 123;
switch (ucWaarde)
{
case 1, 5, 6, 10:
printf ("Case 1");
break;
case 15, 19, 25:
printf ("Case 2");
break;
case 99, 123:
printf ("Case 3");
default:
printf ("Default");
break;
}
|
.
In dat geval wordt er eerst een array gemaakt met de waardes, een array die in totaal 123 (hoogste waarde) bytes zal bevatten. De inhoud zal beginnen met [0,1,0,0,0,1,1,0...], waarbij index 0 de default-switch is, index 1 de eerste case, enzovoorts. De assembly code die hierbij gemaakt wordt moet rekening houden met een aantal dingen: als de waarde hoger dan 123 of lager dan 1 is, zal sowieso de default switch uitgevoerd moeten worden. Verder moet de case-index uit deze eerste translation array opgehaald worden waarna een 2e array gelezen kan worden om het adres van de case uit te vinden. In Assembly zal dit ongeveer gelijk zijn aan;
.
assembly:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| mov eax, 123 ; de case index
cmp eax, 0
jbe _default
cmp eax, 124
jae _default
movsx al, __caseIndex[eax]
jmp __caseAddress[al*4]
_case1:
....
jmp _end
_case2:
....
jmp _end
_case3:
....
_default:
....
_end
; rest van je code |
Dit, samen met de nodige jump en compare instructies, heeft veel meer overhead dan de standaard if-constructie, welke in Assembly taal vrijwel gelijk zal staan aan het volgende. Hierbij wil ik opmerken dat bij echt hele "complexe" switch tabellen, waarbij de waardes meer dan 1000-2000 uit elkaar liggen, de assembler er vaak een grote serie if-compares van maakt.
.
assembly:
1
2
3
4
5
| cmp eax, 123
jne _false
.... ; Code gedeelte voor true
_false:
.... ; Rest van je code |
Uiteindelijk wil ik nog toevoegen dat simpele switches wel degelijk sneller zijn; als je switch goede aaneensluitende waardes bevat, 1-8 bijvoorbeeld, dan wordt er maar één jump gedaan. Dit is ook het geval als het opvolgende waardes zijn, bijvoorbeeld 1,2,4,8,16 etcetera, daar kennen compilers vaak handige trucjes voor. Wat sneller is heeft echt heel veel te maken met de situatie waarin je het gebruikt, gezien een switch naar drie verschillende dingen kan assembleren. Als je echt voor snelheid wilt gaan kan je nog gaan nadenken over inline Assembly, het is moeilijker, maar je kan code wel zeer veel efficienter maken. Eén van de redenen dat de eerste Rollercoaster Tycoon vrij veel tegelijk kon doen op de oudere systemen; het spel bestond voor 99% uit handgeschreven Assembly.
Sorry als m'n verhaal hier en daar wat onduidelijk is, ben nogal bezig met WOGgen op het moment
Edit;
Nog wat algemene tips voor optimalisatie; een belangrijk punt zijn for-loops en de vergelijkwaarde in het tweede deel. Iets wat vaak gedaan wordt is het volgende:
.
C++:
1 2 3 4 5 6 7 8
| std::list <int> listCijfers;
listCijfers.push_back (5);
listCijfers.push_back (10);
for (int i = 0; i < listCijfers.size(); i++)
{
...
}
|
Sommige assemblers (let op: niet allemaal meer) hebben de neiging om na iedere run van de loop opnieuw de size() methode van de list aan te roepen, wat echt veel tijd in beslag kan nemen bij grotere loops. In gevallen als deze, zet de waarde van de size() functie in een tweede locale variable:
C++:
1 2 3 4
| for (int i = 0, j = listCijfers.size (); i < j; i++)
{
}
|
In het meest efficiente geval zal de assembler de "j" in het EDI register zetten, waardoor je inplaats van een collectie calls, mov's en hertellingen van de lijst enkel één compare instructie hebt, met lijsten van miljoenen cijfertjes kan dat echt seconden gaan schelen.
Peter wijzigde dit bericht 24-07-2008 12:41 (13%)