我有以下循环:
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;
,则跳过该条件)?还是简单地再次设置它是否更快? 最佳答案
是否可以进行优化的答案取决于hit
,HitTestMode
和HitTestMode::Content
是什么,以及是否有可能通过调用child->Gather()
来更改它们。
如果那些标识符是编译器可以证明未修改的常量或局部变量,则很有可能将悬挂子表达式hit && HitTestMode == HitTestMode::Content
。
例如,考虑以下示例的可编译版本:
#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;
}
当使用带有-O3优化选项的g ++ 4.8.1(mingw)进行编译时,您将获得以下循环代码段(添加了注释):
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
,则运行只调用child->Gather()
的循环。如果
hitmodetest
更改为作为函数参数传递的变量,因此不再可能通过调用child-Gather(hit)
进行更改,则编译器还将取消对hittestmode
值的检查。循环并跳转到只调用child->Gather()
的循环。对于本地
hittestmode
,使用-O2
会在循环之前计算hit && hittestmode == HitTestMode::Content
并存储到寄存器中,但仍会在每次循环迭代中测试寄存器,而不是优化为一个甚至不会循环的单独循环麻烦测试。由于您是专门询问VS2013编译器(使用
/Ox
和/Ot
选项)的,因此它似乎并没有提升或优化循环中的任何检查(对于hit
或hittestmode
)-似乎要做的是将这些变量的值保留在寄存器中。