Mam ten kod: mainP.c:
int main(int c, char **v){
char *s = v[0];
while (*s++ != 0) {
if ((*s == "a") && (*s != "b")) {
return 1;
}
}
return 0;
}
które kompiluję z clang i gcc generującymi kod asemblera w celu porównania optymalizacji:
clang-3.9 -S -masm=intel -O3 mainP.c
gcc -S -masm=intel -O3 mainP.c
Wersja kompilatora to:
clang version 3.9.1-9 (tags/RELEASE_391/rc2)
Target: x86_64-pc-linux-gnu
gcc (Debian 6.3.0-18) 6.3.0 20170516
2 wynikowe kody zestawu to:
kod zestawu gcc:
main:
.LFB0:
.cfi_startproc
mov rax, QWORD PTR [rsi]
jmp .L2
.p2align 4,,10
.p2align 3
.L4:
cmp BYTE PTR [rax], 97
je .L5
.L2:
add rax, 1
cmp BYTE PTR -1[rax], 0
jne .L4
xor eax, eax
ret
.L5:
mov eax, 1
ret
kod zestawu clang:
main: # @main
.cfi_startproc
# BB#0:
mov rcx, qword ptr [rsi]
mov dl, byte ptr [rcx]
inc rcx
.p2align 4, 0x90
.LBB0_1: # =>This Inner Loop Header: Depth=1
xor eax, eax
test dl, dl
je .LBB0_3
# BB#2: # in Loop: Header=BB0_1 Depth=1
movzx edx, byte ptr [rcx]
inc rcx
mov eax, 1
cmp dl, 97
jne .LBB0_1
.LBB0_3:
ret
Zauważam to: w kodzie zestawu gcc do * s można uzyskać dwa razy w pętli, podczas gdy do * s można uzyskać tylko raz kod zestawu clang.
czy jest wyjaśnienie różnicy?
Następnie po lekkiej zmianie kodu C (dodaniu lokalnej zmiennej char), otrzymuję mniej więcej ten sam kod asemblera z GCC:
int main(int c, char **v){
char *s = v[0];
char ch;
ch = *s;
while (ch != 0) {
if ((ch == "a") && (ch != "b")) {
return 1;
}
ch = *s++;
}
return 0;
}
Wynikowy kod zestawu z GCC:
main:
.LFB0:
.cfi_startproc
mov rax, QWORD PTR [rsi]
movzx edx, BYTE PTR [rax]
test dl, dl
je .L6
add rax, 1
cmp dl, 97
jne .L5
jmp .L8
.p2align 4,,10
.p2align 3
.L4:
cmp dl, 97
je .L8
.L5:
add rax, 1
movzx edx, BYTE PTR -1[rax]
test dl, dl
jne .L4
.L6:
xor eax, eax
ret
.L8:
mov eax, 1
ret
Odpowiedzi:
0 dla odpowiedzi № 1Wyjaśnienie różnicy: optymalizacje kompilatora są ciężko zrobić, a różne kompilatory zoptymalizują Twój kod na różne sposoby.
Ponieważ wyrażenie jest stosunkowo proste, możemy to założyć *s
ma taką samą wartość w obu miejscach i naprawdę potrzebne jest tylko jedno obciążenie.
Ale odkrycie, że optymalizacja jest trudne, ponieważ kompilator musi absolutnie „wiedzieć”, że „cokolwiek wskazuje na”, nie można zmienić między pierwszym odniesieniem a drugim.
W twoim przykładzie clang lepiej jest rozgryźć to niż gcc.