C++ 在C/C++;语句参数的内部算术运算
假设我有以下代码:C++ 在C/C++;语句参数的内部算术运算,c++,c,for-loop,constants,compiler-optimization,C++,C,For Loop,Constants,Compiler Optimization,假设我有以下代码: int v; setV(&v); for (int i = 0; i < v - 5; i++) { // Do stuff here, but don't use v. } 第二种样式会有所不同吗?编辑: 出于意想不到的原因,这是一个有趣的问题。更重要的是,编译器要避免出现意外的v别名这种迟钝的情况。如果编译器能够证明这不会发生(版本2),那么我们就可以得到更好的代码 这里的教训是更关注消除混叠,而不是尝试为它做乐观主义者的工作 制作拷贝cv实际上带来了
int v;
setV(&v);
for (int i = 0; i < v - 5; i++) {
// Do stuff here, but don't use v.
}
第二种样式会有所不同吗?编辑:
出于意想不到的原因,这是一个有趣的问题。更重要的是,编译器要避免出现意外的v
别名这种迟钝的情况。如果编译器能够证明这不会发生(版本2),那么我们就可以得到更好的代码
这里的教训是更关注消除混叠,而不是尝试为它做乐观主义者的工作
制作拷贝cv实际上带来了最大的优化(省去了冗余内存获取),尽管乍一看它似乎(稍微)效率较低
原始答案和演示:
让我们看看:
鉴于:
extern void setV(int*);
extern void do_something(int i);
void test1()
{
int v;
setV(&v);
for (int i = 0; i < v - 5; i++) {
// Do stuff here, but don't use v.
do_something(i);
}
}
void test2()
{
int v;
setV(&v);
const int cv = v;
for (int i = 0; i < cv - 5; i++) {
// Do stuff here. Changing cv is actually impossible.
do_something(i);
}
}
第二种形式在这个编译器上更好。我们希望编译器在这两种情况下都能对此进行优化。但其实只有一种方法可以找到答案。编译它并检查assembly.dependens。是否将
v
的地址传递给循环中的函数?如果是,功能是否在不同的翻译单元中?如果是这样,是否启用了链接时间优化?在我的实际程序中,v是由一个函数引用设置的几个变量之一。它从不在循环内部使用。我不知道如何检查编译后的代码。假设注释“don't use v”没有说谎,任何远程半生不熟的优化器都会检查v
的使用情况。在第一个示例中,请参见表达式v-5
从不更改,因为v
从不更改,也不会将其置于可能更改的位置,并执行一次计算。如果不是这样的话,你的优化器就糟透了。编译到asm,如果你真的担心的话,请查看说明。@WhozCraig我会看看我是否能想出如何编译到asm。(并理解结果)我主要是好奇,而不是担心。我被要求编写高效的代码,但我不确定哪一个是最高效的。在循环之外设置值或这样做。无论如何,我希望能够支持我的决定。目前,我正在使用GCC,但最终它将在VC++上运行。我不知道VC++是否是半生不熟的。请记住,如果编译器没有使用整个程序优化,它就无法在第一种情况下优化掉求值,因为它不知道v
在setV
中没有别名,在do\u something
中没有变异@MarkB v是函数的局部。没有从do_something(按值获取其参数)合法访问v的权限,因此编译器可以像在test2()中那样自由假设没有别名。setV
获取本地v
的地址,并将其粘贴到全局_v_ptr
。然后do\u something
通过*global\u v\u ptr
访问v
,并合法地更新局部变量。@MarkB是的,因此在test2中,我们向编译器证明调用和循环结束条件之间没有因果关系。这本身可能是在第二种形式中使用额外变量的一个成功论点当i
时进行编码>和迭代,为什么不设置cv=v-5并在i
时进行交互?作为一名代码维护人员,如果我看到for构造中的数学知识,我会认为该表达式中的一个(或多个)变量可能会在循环中的某个位置发生变化,这可能是直接的,也可能是函数调用的副作用。
extern void setV(int*);
extern void do_something(int i);
void test1()
{
int v;
setV(&v);
for (int i = 0; i < v - 5; i++) {
// Do stuff here, but don't use v.
do_something(i);
}
}
void test2()
{
int v;
setV(&v);
const int cv = v;
for (int i = 0; i < cv - 5; i++) {
// Do stuff here. Changing cv is actually impossible.
do_something(i);
}
}
test1():
pushq %rbx
subq $16, %rsp
leaq 12(%rsp), %rdi
call setV(int*)
cmpl $5, 12(%rsp)
jle .L1
xorl %ebx, %ebx
.L5:
movl %ebx, %edi
addl $1, %ebx
call do_something(int)
movl 12(%rsp), %eax
subl $5, %eax
cmpl %ebx, %eax
jg .L5
.L1:
addq $16, %rsp
popq %rbx
ret
test2():
pushq %rbp
pushq %rbx
subq $24, %rsp
leaq 12(%rsp), %rdi
call setV(int*)
movl 12(%rsp), %eax
cmpl $5, %eax
jle .L8
leal -5(%rax), %ebp
xorl %ebx, %ebx
.L12:
movl %ebx, %edi
addl $1, %ebx
call do_something(int)
cmpl %ebp, %ebx
jne .L12
.L8:
addq $24, %rsp
popq %rbx
popq %rbp
ret