Tôi đã tuyên bố với một đồng nghiệp if (i < input.size() - 1) print(0);
sẽ được tối ưu hóa trong vòng lặp này để nó input.size()
không được đọc trong mỗi lần lặp, nhưng hóa ra đó không phải là trường hợp!
void print(int x) {
std::cout << x << std::endl;
}
void print_list(const std::vector<int>& input) {
int i = 0;
for (size_t i = 0; i < input.size(); i++) {
print(input[i]);
if (i < input.size() - 1) print(0);
}
}
Theo Compiler Explorer với các tùy chọn gcc, -O3 -fno-exceptions
chúng tôi thực sự đang đọc input.size()
từng vòng lặp và sử dụng lea
để thực hiện phép trừ!
movq 0(%rbp), %rdx
movq 8(%rbp), %rax
subq %rdx, %rax
sarq $2, %rax
leaq -1(%rax), %rcx
cmpq %rbx, %rcx
ja .L35
addq $1, %rbx
Thật thú vị, trong Rust tối ưu hóa này xảy ra. Dường như i
được thay thế bằng một biến j
được giảm dần mỗi lần lặp và thử nghiệm i < input.size() - 1
được thay thế bằng một cái gì đó như thế j > 0
.
fn print(x: i32) {
println!("{}", x);
}
pub fn print_list(xs: &Vec<i32>) {
for (i, x) in xs.iter().enumerate() {
print(*x);
if i < xs.len() - 1 {
print(0);
}
}
}
Trong Compiler Explorer , hội đồng có liên quan trông như thế này:
cmpq %r12, %rbx
jae .LBB0_4
Tôi đã kiểm tra và tôi khá chắc chắn r12
là xs.len() - 1
và rbx
là quầy. Trước đó có một add
for rbx
và mov
bên ngoài của vòng lặp vào r12
.
Tại sao lại thế này? Có vẻ như nếu GCC có thể nội tuyến size()
và operator[]
như đã làm, nó sẽ có thể biết rằng điều size()
đó không thay đổi. Nhưng có lẽ trình tối ưu hóa của GCC đánh giá rằng không đáng để kéo nó ra thành một biến? Hoặc có thể có một số tác dụng phụ có thể khác sẽ làm cho điều này không an toàn - có ai biết không?
cout.operator<<()
. Trình biên dịch không biết rằng hàm hộp đen này không nhận được tham chiếu đến std::vector
từ toàn cầu.
println
hoặc operator<<
là chính.
println
có thể là một phương thức phức tạp, trình biên dịch có thể gặp khó khăn khi chứng minh rằngprintln
không làm biến đổi vectơ.