我要求一个if (i < input.size() - 1) print(0);
在此循环中得到优化的同事,这样input.size()
就不会在每次迭代中都读取它,但事实证明并非如此!
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);
}
}
根据带有gcc选项的Compiler Explorer,-O3 -fno-exceptions
我们实际上是在读取input.size()
每个迭代并lea
用于执行减法运算!
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
有趣的是,在Rust中确实发生了这种优化。看起来好像i
被替换j
为每次迭代递减的变量,并且测试i < input.size() - 1
被替换为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);
}
}
}
在编译器资源管理器中,相关程序集如下所示:
cmpq %r12, %rbx
jae .LBB0_4
我检查,我敢肯定r12
是xs.len() - 1
和rbx
是计数器。之前有add
for rbx
和mov
in循环之外r12
。
为什么是这样?看起来,如果GCC能够内联size()
和operator[]
,它应该能够知道size()
并不会改变。但是,也许GCC的优化程序判断是否值得将其提取为变量?也许还有其他可能导致这种情况不安全的副作用-有人知道吗?
cout.operator<<()
。编译器不知道该黑盒函数没有std::vector
从全局引用。
println
or 的复杂性operator<<
是关键。
println
可能是一个复杂的方法,编译器可能无法证明println
不会使向量发生变异。