Trong khi viết một ftol
chức năng tối ưu hóa, tôi tìm thấy một số hành vi rất kỳ quặc GCC 4.6.1
. Hãy để tôi chỉ cho bạn mã đầu tiên (để rõ ràng tôi đã đánh dấu sự khác biệt):
nhanh_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 */
}
nhanh_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 */
}
Có vẻ giống nhau phải không? GCC cũng không đồng ý. Sau khi biên dịch với gcc -O3 -S -Wall -o test.s test.c
đây là đầu ra lắp ráp:
fast_trunc_one, được tạo:
_fast_trunc_one:
LFB0:
.cfi_startproc
movl 4(%esp), %eax
movl $150, %ecx
movl %eax, %edx
andl $8388607, %edx
sarl $23, %eax
orl $8388608, %edx
andl $255, %eax
subl %eax, %ecx
movl %edx, %eax
sarl %cl, %eax
testl %ecx, %ecx
js L5
rep
ret
.p2align 4,,7
L5:
negl %ecx
movl %edx, %eax
sall %cl, %eax
ret
.cfi_endproc
fast_trunc_two, được tạo:
_fast_trunc_two:
LFB1:
.cfi_startproc
pushl %ebx
.cfi_def_cfa_offset 8
.cfi_offset 3, -8
movl 8(%esp), %eax
movl $150, %ecx
movl %eax, %ebx
movl %eax, %edx
sarl $23, %ebx
andl $8388607, %edx
andl $255, %ebx
orl $8388608, %edx
andl $-2147483648, %eax
subl %ebx, %ecx
js L9
sarl %cl, %edx
movl %eax, %ecx
negl %ecx
xorl %ecx, %edx
addl %edx, %eax
popl %ebx
.cfi_remember_state
.cfi_def_cfa_offset 4
.cfi_restore 3
ret
.p2align 4,,7
L9:
.cfi_restore_state
negl %ecx
sall %cl, %edx
movl %eax, %ecx
negl %ecx
xorl %ecx, %edx
addl %edx, %eax
popl %ebx
.cfi_restore 3
.cfi_def_cfa_offset 4
ret
.cfi_endproc
Đó là một sự khác biệt cực kỳ . Điều này thực sự cũng xuất hiện trên hồ sơ, fast_trunc_one
nhanh hơn khoảng 30% fast_trunc_two
. Bây giờ câu hỏi của tôi: điều gì gây ra điều này?
-S -O3 -da -fdump-tree-all
. Điều này sẽ tạo ra nhiều ảnh chụp nhanh của đại diện trung gian. Đi qua chúng (chúng được đánh số) cạnh nhau và bạn sẽ có thể tìm thấy tối ưu hóa còn thiếu trong trường hợp đầu tiên.
int
thành unsigned int
và xem sự khác biệt biến mất.
(r + shifted) ^ sign
không giống như r + (shifted ^ sign)
. Tôi đoán đó là nhầm lẫn tối ưu hóa? FWIW, MSVC 2010 (16.00.40219.01) tạo ra các danh sách gần giống với nhau: gist.github.com/2430454