Um mal kurz ein Beispiel für den Unterschied was Optimierungen bei FPC und GCC angeht zu geben, simples Fibonacci program:
Code: Alles auswählen
function fib(const n: Cardinal): Cardinal;
begin
if n = 0 then Exit(1);
if n = 1 then Exit(1);
Result := fib(n - 1) + fib(n - 2);
end;
In O1:
Code: Alles auswählen
fib(longword):
pushq %rbp
movq %rsp,%rbp
leaq -32(%rsp),%rsp
movq %rbx,-24(%rbp)
movl %edi,-8(%rbp)
testl %edi,%edi
jne .Lj6
movl $1,-12(%rbp)
jmp .Lj3
.Lj6:
cmpl $1,-8(%rbp)
jne .Lj8
movl $1,-12(%rbp)
jmp .Lj3
.Lj8:
movl -8(%rbp),%eax
leal -1(%eax),%edi
call fib(longword)
movl %eax,%ebx
movl -8(%rbp),%eax
leal -2(%eax),%edi
call fib(longword)
addl %ebx,%eax
movl %eax,-12(%rbp)
.Lj3:
movl -12(%rbp),%eax
movq -24(%rbp),%rbx
movq %rbp,%rsp
popq %rbp
ret
O3:
Code: Alles auswählen
fib(longword):
pushq %rbx
pushq %r12
pushq %r13
movl %edi,%ebx
testl %edi,%edi
jne .Lj6
movl $1,%r12d
jmp .Lj3
.Lj6:
cmpl $1,%ebx
jne .Lj8
movl $1,%r12d
jmp .Lj3
.Lj8:
movl %ebx,%edi
subl $1,%edi
call fib(longword)
movl %eax,%r13d
movl %ebx,%edi
subl $2,%edi
call fib(longword)
leal (%eax,%r13d),%r12d
.Lj3:
movl %r12d,%eax
popq %r13
popq %r12
popq %rbx
ret
Im grunde extrem ähnlich, also optimierungstechnisch passiert hier nicht viel.
GCC:
Code: Alles auswählen
unsigned fib(unsigned const n) {
if (n == 0) return 1;
if (n == 1) return 1;
return fib(n - 1) + fib(n - 2);
}
O1:
Code: Alles auswählen
fib(unsigned int):
mov eax, 1
cmp edi, 1
ja .L8
ret
.L8:
push rbp
push rbx
sub rsp, 8
mov ebx, edi
lea edi, [rdi-1]
call fib(unsigned int)
mov ebp, eax
lea edi, [rbx-2]
call fib(unsigned int)
add eax, ebp
add rsp, 8
pop rbx
pop rbp
ret
O3:
Code: Alles auswählen
fib(unsigned int):
cmp edi, 1
jbe .L20
push r15
mov r10d, edi
push r14
push r13
push r12
push rbp
push rbx
sub rsp, 56
mov DWORD PTR [rsp], 0
.L19:
mov DWORD PTR [rsp+4], 0
lea r11d, [r10-1]
mov DWORD PTR [rsp+24], r10d
.L18:
cmp r11d, 1
je .L3
mov DWORD PTR [rsp+8], 0
lea r9d, [r11-1]
mov DWORD PTR [rsp+28], r11d
.L17:
cmp r9d, 1
je .L4
mov DWORD PTR [rsp+32], r9d
lea r15d, [r9-1]
xor r10d, r10d
.L16:
cmp r15d, 1
je .L5
mov DWORD PTR [rsp+40], r10d
lea edx, [r15-1]
xor r11d, r11d
mov DWORD PTR [rsp+36], r15d
mov r14d, r11d
mov r15d, edx
.L15:
cmp r15d, 1
je .L6
lea r12d, [r15-1]
xor esi, esi
mov ecx, r14d
mov r8d, r15d
.L14:
cmp r12d, 1
je .L7
lea ebx, [r12-1]
xor r13d, r13d
mov edx, r12d
.L13:
cmp ebx, 1
je .L8
mov eax, ebx
lea r14d, [rbx-1]
xor r15d, r15d
mov ebx, ecx
mov ecx, r13d
mov r13d, esi
mov esi, eax
.L12:
cmp r14d, 1
je .L9
mov DWORD PTR [rsp+44], r14d
lea ebp, [r14-1]
xor r12d, r12d
mov r14d, edx
.L11:
cmp ebp, 1
je .L10
lea edi, [rbp-1]
mov DWORD PTR [rsp+20], esi
mov DWORD PTR [rsp+16], r8d
mov DWORD PTR [rsp+12], ecx
call fib(unsigned int)
mov ecx, DWORD PTR [rsp+12]
mov r8d, DWORD PTR [rsp+16]
add r12d, eax
sub ebp, 2
mov esi, DWORD PTR [rsp+20]
jne .L11
.L10:
mov edx, r14d
mov r14d, DWORD PTR [rsp+44]
lea r15d, [r15+1+r12]
sub r14d, 2
jne .L12
.L9:
mov eax, esi
mov esi, r13d
mov r13d, ecx
mov ecx, ebx
mov ebx, eax
lea r13d, [r15+1+r13]
sub ebx, 2
jne .L13
.L8:
mov r12d, edx
lea esi, [rsi+1+r13]
sub r12d, 2
jne .L14
.L7:
mov r15d, r8d
lea r14d, [rcx+1+rsi]
sub r15d, 2
jne .L15
.L6:
mov r10d, DWORD PTR [rsp+40]
mov r15d, DWORD PTR [rsp+36]
lea r10d, [r10+1+r14]
sub r15d, 2
jne .L16
.L5:
mov eax, DWORD PTR [rsp+8]
mov r9d, DWORD PTR [rsp+32]
lea eax, [rax+1+r10]
mov DWORD PTR [rsp+8], eax
sub r9d, 2
jne .L17
.L4:
mov eax, DWORD PTR [rsp+4]
mov edi, DWORD PTR [rsp+8]
mov r11d, DWORD PTR [rsp+28]
lea eax, [rax+1+rdi]
mov DWORD PTR [rsp+4], eax
sub r11d, 2
jne .L18
.L3:
mov eax, DWORD PTR [rsp]
mov r10d, DWORD PTR [rsp+24]
mov edi, DWORD PTR [rsp+4]
sub r10d, 2
lea eax, [rax+1+rdi]
mov DWORD PTR [rsp], eax
cmp r10d, 1
ja .L19
add rsp, 56
add eax, 1
pop rbx
pop rbp
pop r12
pop r13
pop r14
pop r15
ret
.L20:
mov eax, 1
ret
Der GCC optimizer macht hier eine menge an parses mit verschiedenen substitutionsregeln, loop unrolling, etc. Im grunde wird hier die Funktion mehrfach neugeschrieben bis man am ende nicht mehr erkennen kann was es ursprünglich mal war. Der FPC Optimiser hingegen verbessert ein bisschen den code, aber im grunde bleibt es die selbe Funktion mit ein paar kleinen Änderungen. Das benötigt natürlich deutlich weniger parses.
Das alleine macht wahrscheinlich schon einen nicht unerheblichen unterschied in der Compilezeit aus