Neden GCC neredeyse aynı C kodu için tamamen farklı böyle bir Kurul oluşturur mu?
ftol
optimize edilmiş bir fonksiyon yazarken GCC 4.6.1
çok tuhaf davranışları buldum. Bana kod ilk (netlik farkları işaretledim için) göstereyim:
fast_trunc_one, C:
int fast_trunc_one(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = mantissa << -exponent; /* diff */
} else {
r = mantissa >> exponent; /* diff */
}
return (r ^ -sign) sign; /* diff */
}
fast_trunc_two, C:
int fast_trunc_two(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = (mantissa << -exponent) ^ -sign; /* diff */
} else {
r = (mantissa >> exponent) ^ -sign; /* diff */
}
return r sign; /* diff */
}
Aynı değil mi? İyi GCC aynı fikirde değil. gcc -O3 -S -Wall -o test.s test.c
ile derleme sonra bu derleme çıktı
fast_trunc_one, oluşturulan:
_fast_trunc_one:
LFB0:
.cfi_startproc
movl 4(%esp), �x
movl $150, �x
movl �x, �x
andl $8388607, �x
sarl $23, �x
orl $8388608, �x
andl $255, �x
subl �x, �x
movl �x, �x
sarl %cl, �x
testl �x, �x
js L5
rep
ret
.p2align 4,,7
L5:
negl �x
movl �x, �x
sall %cl, �x
ret
.cfi_endproc
fast_trunc_two, oluşturulan:
_fast_trunc_two:
LFB1:
.cfi_startproc
pushl �x
.cfi_def_cfa_offset 8
.cfi_offset 3, -8
movl 8(%esp), �x
movl $150, �x
movl �x, �x
movl �x, �x
sarl $23, �x
andl $8388607, �x
andl $255, �x
orl $8388608, �x
andl $-2147483648, �x
subl �x, �x
js L9
sarl %cl, �x
movl �x, �x
negl �x
xorl �x, �x
addl �x, �x
popl �x
.cfi_remember_state
.cfi_def_cfa_offset 4
.cfi_restore 3
ret
.p2align 4,,7
L9:
.cfi_restore_state
negl �x
sall %cl, �x
movl �x, �x
negl �x
xorl �x, �x
addl �x, �x
popl �x
.cfi_restore 3
.cfi_def_cfa_offset 4
ret
.cfi_endproc
Biraşırıfark. Aslında bu profil üzerinde de, fast_trunc_one
0 civarında daha hızlı fast_trunc_two
daha gösterir. Bu neden oluyor? şimdi sorum şu:
CEVAP
OP ile eşitlemek için güncellendi düzenlemek
Kodu ile müdahalesi, GCC ilk dava optimize nasıl görmek başardım.
Bu kadar farklı olduğunu anlamak için önce fast_trunc_one()
optimize nasıl anlamak gerekir.
İster inan ister inanma fast_trunc_one()
Bu optimize ediliyor:
int fast_trunc_one(int i) {
int mantissa, exponent;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
if (exponent < 0) {
return (mantissa << -exponent); /* diff */
} else {
return (mantissa >> exponent); /* diff */
}
}
Bu orijinal olarak aynı Meclisi fast_trunc_one()
- kayıt adları ve her şeyi üretir.
fast_trunc_one()
derleme xor
s yok dikkat edin. Bu benim için onu verdim.
Nasıl yani?
Adım 1:sign = -sign
Önce sign
değişken bir göz atalım. sign = i & 0x80000000;
beri sign
alabileceği sadece iki değer vardır:
sign = 0
sign = 0x80000000
Şimdi her iki durumda da, sign == -sign
farkındayız. Bu orijinal kodu değiştirdiğimde bu nedenle:
int fast_trunc_one(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = mantissa << -exponent;
} else {
r = mantissa >> exponent;
}
return (r ^ sign) sign;
}
fast_trunc_one()
orijinal tam olarak aynı montaj üretir. Derleme bağışlarım, ama aynı isim ve kayıt.
Adım 2:Matematiksel azaltma: x (y ^ x) = y
sign
yalnızca iki değer 0
0x80000000
birini alabilir.
x = 0
x (y ^ x) = y
o zaman önemsiz bir tutar.- Ve
0x80000000
ekleyerek yazılmış bir oyun programı. aynı durum. İşaret biti değişiyor. Bu nedenlex (y ^ x) = y
x = 0x80000000
zaman tutar.
Bu nedenle, x (y ^ x)
y
azaltır. Ve bu kodu.:
int fast_trunc_one(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = (mantissa << -exponent);
} else {
r = (mantissa >> exponent);
}
return r;
}
Yine aynı Meclis - kayıt adları ve tüm derler.
Bu yukarıdaki sürümü nihayet bu azaltır:
int fast_trunc_one(int i) {
int mantissa, exponent;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
if (exponent < 0) {
return (mantissa << -exponent); /* diff */
} else {
return (mantissa >> exponent); /* diff */
}
}
tam olarak GCC derleme oluşturur zaten.
Neden derleyici aynı şeyi fast_trunc_two()
optimize değil?
fast_trunc_one()
anahtar rol x (y ^ x) = y
optimizasyonu. fast_trunc_two()
50 *ifade Şubesi arasında bölünmüş ediliyor.
GCC bu optimizasyonu yapmak için değil, şaşırtmak için yeterli olabilir sanırım. (Şube ^ -sign
vinç için gerekir ve bu birleştirme sonunda r sign
içine.)
Örneğin, bu fast_trunc_one()
aynı montaj üretir:
int fast_trunc_two(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = ((mantissa << -exponent) ^ -sign) sign; /* diff */
} else {
r = ((mantissa >> exponent) ^ -sign) sign; /* diff */
}
return r; /* diff */
}
Neden aynı hat üzerinde anonim bir fon...
Neden gcc eğer hız yerine BOYUTU için ...
# 5 CTP uyumsuz C: neden iç "devl...
Neden Java bize bir isim dosya adından...
Nasıl bir Java HashMap aynı karma kodu...