我有以下循环:
for (unique_ptr<Surface>& child : Children)
{
child->Gather(hit);
if (hit && HitTestMode == HitTestMode::Content && child->MouseOver && !mouseOver)
{
mouseOver = true;
}
}
我想知道编译器(我使用Visual Studio 2013,在Win7上向上定位x64)是否会优化表达式
hit && HitTestMode == HitTestMode::Content
进入一个临时常量并使用它而不是每次迭代解析表达式,类似于我做这样的事情:
bool contentMode = hit && HitTestMode == HitTestMode::Content;
for (unique_ptr<Surface>& child : Children)
{
child->Gather(hit);
if (contentMode && child->MouseOver && !mouseOver)
{
mouseOver = true;
}
}
奖金问题:
检查!mouseOver
是否值得(为了跳过条件mouseOver = true;
,如果已经设置了)?或者无论如何简单地再次设置它会更快吗?
答案 0 :(得分:1)
是否可以进行优化的答案取决于hit
,HitTestMode
和HitTestMode::Content
是什么以及是否可以通过调用{{{ 1}}。
如果这些标识符是编译器可以证明的常量或局部变量未被修改,那么完全有可能将提升子表达式child->Gather()
。
例如,请考虑以下可编辑的示例版本:
hit && HitTestMode == HitTestMode::Content
使用带有-O3优化选项的g ++ 4.8.1(mingw)编译时,您将获得以下代码片段(添加了注释):
#include <memory>
#include <vector>
using namespace std;
class Surface
{
public:
void Gather(bool hit);
bool MouseOver;
};
enum class HitTestMode
{
Content = 1,
Foo = 3,
Bar = 4,
};
extern HitTestMode hittestmode;
bool anyMiceOver( vector<unique_ptr<Surface> > & Children, bool hit)
{
bool mouseOver = false;
for (unique_ptr<Surface>& child : Children)
{
child->Gather(hit);
if (hit && hittestmode == HitTestMode::Content && child->MouseOver && !mouseOver)
{
mouseOver = true;
}
}
return mouseOver;
}
您会注意到 mov rbx, QWORD PTR [rcx] ; Children.begin()
mov rsi, QWORD PTR 8[rcx] ; Children.end()
cmp rbx, rsi
je .L8 ; early exit if Children is empty
test dl, dl ; hit == 0?
movzx edi, dl
je .L5 ; then goto loop L5
xor ebp, ebp
mov r12d, 1
jmp .L7
.p2align 4,,10
.L6:
add rbx, 8
cmp rsi, rbx ; check for end of Children
je .L2
.L7:
mov rcx, QWORD PTR [rbx]
mov edx, edi
call _ZN7Surface6GatherEb ; call child->Gather(hit)
cmp DWORD PTR hittestmode[rip], 1 ; check hittestmode
jne .L6
mov rax, QWORD PTR [rbx] ; check child->MouseOver
cmp BYTE PTR [rax], 0
cmovne ebp, r12d ; set mouseOver appropriately
jmp .L6
.p2align 4,,10
.L5: ; loop L5 is run only when hit == 0
mov rcx, QWORD PTR [rbx] ; get net element in Children
mov edx, edi
add rbx, 8
call _ZN7Surface6GatherEb ; call child->Gather(hit)
cmp rsi, rbx
jne .L5
.L8:
xor ebp, ebp
.L2:
mov eax, ebp
add rsp, 32
pop rbx
pop rsi
pop rdi
pop rbp
pop r12
ret
的检查已经从循环中提升 - 如果它是hit
,则会运行除了调用false
之外什么都不做的循环。< / p>
如果将child->Gather()
更改为一个作为函数参数传递的变量,那么它不再可能被hitmodetest
的调用更改,那么编译器也会提升检查child-Gather(hit)
的值离开循环并跳转到除了调用hittestmode
之外什么都不做的循环。
使用child->Gather()
使用hittestmode
将在循环之前计算-O2
并将结果存入寄存器,但它仍会在每次循环迭代中测试寄存器而不是优化到一个单独的循环,甚至不打扰测试。
由于您特别询问了VS2013编译器(使用hit && hittestmode == HitTestMode::Content
和/Ox
选项),因此它似乎没有提升或优化任何一项检查(对于/Ot
或{{ 1}})离开循环 - 它似乎只是将这些变量的值保存在寄存器中。