编译器会将此表达式优化为临时常量而不是每次迭代都解决它吗?

时间:2014-09-20 23:55:11

标签: c++ loops optimization expression

我有以下循环:

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;,如果已经设置了)?或者无论如何简单地再次设置它会更快吗?

1 个答案:

答案 0 :(得分:1)

是否可以进行优化的答案取决于hitHitTestModeHitTestMode::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}})离开循环 - 它似乎只是将这些变量的值保存在寄存器中。