x86-32 mit Optimierung auf Größe:
Ich habe mir nun mal das Disassembly einer Funktion angeschaut, die von 1483 B auf 1131 B geschrumpft ist (sage und schreibe 24 %!) …
Optimiert wurden keinesfalls triviale
switch-Konstruktionen à:
switch(param) {
case 0: return "foo";
case 1: return "bar";
case 2: …
… die waren auch vorher schon ziemlich optimal. Nein, stattdessen werden jetzt
verschiedene Unterzweige zusammengefasst. Sagen wir etwa:
char buf[…];
switch(errorType) {
case CError:
strcpy(buf, strerr(errno));
break;
case CppException:
strcpy(buf, exceptionObject.str());
break;
default:
strcpy(buf, "unknown");
break;
Obwohl die drei Pfade in der SSA drastisch unterschiedlich sind (einer erzeugt einen C++-String, dar andere nicht, und der dritte läuft komplett über Konstanten) wird bei mir nun das
strcpy() zusammengefasst. Wenn man bedenkt, dass es zuvor völlig unterschiedliche Register und völlig unterschiedliche Stack-Quellen benutzt hat, ist das schon recht beeindruckend. Ich sehe sogar Code, der bei Strings die Null am Ende mitkopiert, mit Code zusammengefaltet, der sie überspringt, wenn genug überlappende Befehle existieren. Es fällt mir zunehmend schwer, da etwas Negatives festzustellen.
x86-64 mit Optimierung auf Größe:
Sprach ich von Negativem? Ach ja, die 64-Bit-Version. Dieser Code:
auto end = copyUntilZero(message, "D3D info: GPU supports Direct3D ");
switch(USize(more)) {
case 0x9100: copyZeroTerminated(end, "9" ); break;
case 0x9200: copyZeroTerminated(end, "9.0b"); break;
case 0x9300: copyZeroTerminated(end, "9.0c"); break;
case 0xA000: copyZeroTerminated(end, "10" ); break;
case 0xA100: copyZeroTerminated(end, "10.1"); break;
case 0xB000: copyZeroTerminated(end, "11" ); break;
case 0xB100: copyZeroTerminated(end, "11.1"); break;
case 0xB200: copyZeroTerminated(end, "11.2"); break;
case 0xB300: copyZeroTerminated(end, "11.3"); break;
case 0xB400: copyZeroTerminated(end, "11.4"); break;
case 0xC000: copyZeroTerminated(end, "12" ); break;
case 0xC100: copyZeroTerminated(end, "12.1"); break;
default: UNREACHABLE;
}
… sieht auf x86 für Größe perfekt aus:
Code: Alles auswählen
mov edx,offset string "D3D info: GPU supports Direct3D@"... (01C1E0h)
lea ecx,[message]
mov al,44h
cbw
mov word ptr [ecx],ax
add ecx,2
inc edx
mov al,byte ptr [edx]
test al,al
jne UTF16Supervisor::forwardEventAsUTF16+2FEh (015E35h)
mov eax,0B100h
cmp edi,eax
ja UTF16Supervisor::forwardEventAsUTF16+382h (015EB9h)
je UTF16Supervisor::forwardEventAsUTF16+378h (015EAFh)
cmp edi,9100h
je UTF16Supervisor::forwardEventAsUTF16+36Eh (015EA5h)
cmp edi,9200h
je UTF16Supervisor::forwardEventAsUTF16+364h (015E9Bh)
cmp edi,9300h
je UTF16Supervisor::forwardEventAsUTF16+35Ah (015E91h)
cmp edi,0A000h
je UTF16Supervisor::forwardEventAsUTF16+350h (015E87h)
mov edx,offset string "11" (01C224h)
mov eax,offset string "10.1" (01C21Ch)
cmp edi,0A100h
cmove edx,eax
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "10" (01C218h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "9.0c" (01C210h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "9.0b" (01C208h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "9" (01C204h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "11.1" (01C228h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
cmp edi,0B200h
je UTF16Supervisor::forwardEventAsUTF16+3C0h (015EF7h)
cmp edi,0B300h
je UTF16Supervisor::forwardEventAsUTF16+3B6h (015EEDh)
cmp edi,0B400h
je UTF16Supervisor::forwardEventAsUTF16+3ACh (015EE3h)
mov edx,offset string "12.1" (01C24Ch)
mov eax,offset string "12" (01C248h)
cmp edi,0C000h
jmp UTF16Supervisor::forwardEventAsUTF16+348h (015E7Fh)
mov edx,offset string "11.4" (01C240h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "11.3" (01C238h)
jmp UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov edx,offset string "11.2" (01C230h)
53 Befehle, und dazu recht kompakte. Jetzt die x64-Version mit den selben Einstellungen, ebenfalls für Größe(!) optimiert:
Code: Alles auswählen
lea rdx,[string "D3D info: GPU supports Direct3D@"... (01E530h)]
mov al,44h
lea rcx,[message]
inc rdx
movsx eax,al
mov word ptr [rcx],ax
add rcx,2
mov al,byte ptr [rdx]
test al,al
jne UTF16Supervisor::forwardEventAsUTF16+3DFh (0168A7h)
mov eax,0B100h
cmp r8,rax
ja UTF16Supervisor::forwardEventAsUTF16+535h (0169FDh)
je UTF16Supervisor::forwardEventAsUTF16+50Fh (0169D7h)
cmp r8,9100h
je UTF16Supervisor::forwardEventAsUTF16+4E9h (0169B1h)
cmp r8,9200h
je UTF16Supervisor::forwardEventAsUTF16+4C3h (01698Bh)
cmp r8,9300h
je UTF16Supervisor::forwardEventAsUTF16+49Dh (016965h)
mov eax,31h
mov word ptr [rcx],ax
cmp r8,0A000h
je UTF16Supervisor::forwardEventAsUTF16+47Fh (016947h)
cmp r8,0A100h
je UTF16Supervisor::forwardEventAsUTF16+461h (016929h)
lea rdx,[string "11" (01E574h)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+44Ah (016912h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
lea rdx,[string "10.1" (01E56Ch)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+468h (016930h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
lea rdx,[string "10" (01E568h)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+486h (01694Eh)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
mov eax,39h
lea rdx,[string "9.0c" (01E560h)]
mov word ptr [rcx],ax
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+4ACh (016974h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
mov eax,39h
lea rdx,[string "9.0b" (01E558h)]
mov word ptr [rcx],ax
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+4D2h (01699Ah)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
mov eax,39h
lea rdx,[string "9" (01E554h)]
mov word ptr [rcx],ax
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+4F8h (0169C0h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
mov eax,31h
lea rdx,[string "11.1" (01E578h)]
mov word ptr [rcx],ax
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+51Eh (0169E6h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
mov eax,31h
mov word ptr [rcx],ax
cmp r8,0B200h
je UTF16Supervisor::forwardEventAsUTF16+5DDh (016AA5h)
cmp r8,0B300h
je UTF16Supervisor::forwardEventAsUTF16+5BFh (016A87h)
cmp r8,0B400h
je UTF16Supervisor::forwardEventAsUTF16+5A1h (016A69h)
cmp r8,0C000h
je UTF16Supervisor::forwardEventAsUTF16+583h (016A4Bh)
lea rdx,[string "12.1" (01E59Ch)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+56Ch (016A34h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
lea rdx,[string "12" (01E598h)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+58Ah (016A52h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
lea rdx,[string "11.4" (01E590h)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+5A8h (016A70h)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
lea rdx,[string "11.3" (01E588h)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+5C6h (016A8Eh)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
lea rdx,[string "11.2" (01E580h)]
inc rdx
lea rcx,[rcx+2]
movsx eax,byte ptr [rdx]
mov word ptr [rcx],ax
cmp byte ptr [rdx],r14b
jne UTF16Supervisor::forwardEventAsUTF16+5E4h (016AACh)
jmp UTF16Supervisor::forwardEventAsUTF16+691h (016B59h)
140 Befehle. Und alles Duplikate. Über drei Mal so groß wie die 32-Bit-Version. DREI FUCKING MAL. Ohne Grund, einfach because FUCK YOU, that’s why. I can’t even …
Wirklich, vergesst den 64-Bit-Compiler, wenn ihr nach Größe geht. Und das ist so eine riesen Verschwendung, denn theoretisch könnte die 64-Bit-Version durch geringeren Registerdruck sogar überlegenen Code produzieren. Ich habe das Gefühl, dass das noch nie jemand bei Microsoft getestet hat und wenn ich’s isoliert kriege, wird das ein fetter Bug Report.
Davon ab hat sich mal wieder die Gewichtung für Inlining geändert. Das hatten wir doch letztens erst. Warum
mit jedem Release? Und es wird irgendwie auch überhaupt nicht
besser dadurch …
… okay, auch mal was Positives: Die Register Allocation hat sich
wirklich verbessert. In der Größenordnung von 1 % sogar, also deutlich über dem Rauschen, in dem die letzten Verbesserungen untergingen. Ich sehe weniger MOV und weniger Registerdruck gerade bei vielen Integer-Operationen. Sehr vereinzelt, aber in der Summe mit stattlichem Ergebnis.
x86 und x64 für Geschwindigkeit:
Habe leider gerade keine Benchmarks parat. Die Größe der Executables ist jedenfalls leicht gesunken – viel weniger Befehle (wahrscheinlich die gleichen SSA-Verbesserungen wie bei der Optimierung für Größe), aber dafür mehr statische Daten (?!?). Hmm.