Phân tách số nguyên /
và modulo %
hoạt động thường được sử dụng cùng nhau trong lập trình, đôi khi ngay cả trên cùng toán hạng và các dòng tiếp theo. Ví dụ, hàm C sau đây, mà là một chức năng đơn giản mà tóm tắt kết quả của một /
của 2 số với kết quả của %
của họ, không chỉ rằng:Tối ưu hóa các cuộc gọi tiếp theo đến số nguyên và modulo (phần còn lại)
int sum2digits(int x, int base) {
int n, m;
n = x/base;
m = x % base;
return n + m;
}
Như tôi biết, cả hai /
và %
là được thực hiện bởi cùng một lệnh máy (trong x86). Giả sử, nếu bạn thực hiện lệnh máy để chia số nguyên (div
hoặc idiv
) của hai số, a
và b
, thì sau đó giá trị a/b
sẽ được lưu trữ trong EAX đăng ký và số còn lại a % b
trong EDX.
Tôi tự hỏi liệu trình biên dịch có tận dụng được chất lượng này và xem xét mã lắp ráp hay không. Nó chỉ ra rằng biên soạn bình thường với gcc không tối ưu hóa này:
push %rbp
mov %rsp,%rbp
mov %edi,-0x14(%rbp)
mov %esi,-0x18(%rbp)
mov -0x14(%rbp),%eax
mov %eax,%edx
sar $0x1f,%edx
idivl -0x18(%rbp)
mov %eax,-0x8(%rbp)
mov -0x14(%rbp),%eax
mov %eax,%edx
sar $0x1f,%edx
idivl -0x18(%rbp)
mov %edx,-0x4(%rbp)
mov -0x4(%rbp),%eax
mov -0x8(%rbp),%edx
add %edx,%eax
pop %rbp
retq
mã lắp ráp này 2 cuộc gọi tiếp theo để idivl, nhưng mỗi lần đọc kết quả từ đăng ký khác (EAX cho thương, EDX cho phần còn lại). Tuy nhiên, biên soạn với -O
thay đổi hình ảnh:
mov %edi,%eax
mov %edi,%edx
sar $0x1f,%edx
idiv %esi
add %edx,%eax
retq
Mã này gọi idiv
chỉ một lần, và sử dụng giá trị của nó cho cả tính toán.
Tại sao loại tối ưu hóa này không được mặc định? Việc sử dụng gọi div
hai lần liên tiếp là gì? Tối ưu hóa này có thể thay đổi hành vi của một chương trình theo bất kỳ cách nào không? Ngoài ra, và có lẽ quan trọng hơn, là có cách nào, với tư cách là lập trình viên, để trích xuất thủ công 2 giá trị này (thương và phần dư) đảm bảo rằng chỉ có 1 phân chia số nguyên được thực hiện bởi CPU?
Theo mặc định, GCC vô hiệu hóa tất cả các tối ưu hóa *** trừ khi bạn chỉ định '-O'. Không chỉ cái này ... – Mysticial
Tôi đã xóa câu trả lời của mình, vì tôi không thể làm cho nó hoạt động. Mặt khác, nếu bạn nói 'return x/base + x% base;' và bật tối ưu hóa trình biên dịch, bạn sẽ nhận được việc thực hiện hiệu quả. –
Với GCC bạn có thể sử dụng lắp ráp nội tuyến để đảm bảo rằng bạn nhận được cả hai kết quả từ một bộ phận, nhưng tôi không thể bị làm phiền để tìm kiếm các chi tiết, và tôi đã không sử dụng lắp ráp nội tuyến trong một thời gian ... –