Do C++ compilers perform compile-time optimizations on lambda closures?

后端 未结 2 1853
粉色の甜心
粉色の甜心 2021-02-20 00:01

Suppose we have the following (nonsensical) code:

const int a = 0;
int c = 0;
for(int b = 0; b < 10000000; b++)
{
    if(a) c++;
    c += 7;
}
2条回答
  •  滥情空心
    2021-02-20 00:51

    Both gcc at -O3 and MSVC2015 Release won't optimize it away with this simple code and the lambda would actually be called

    #include 
    #include 
    
    int main()
    {
        int a = 0;    
        std::function lambda = [a]()
        {
            int c = 0;
            for(int b = 0; b < 10; b++)
            {
                if(a) c++;
                c += 7;
            }
            return c;
        };
    
        std::cout << lambda();
    
        return 0;
    }
    

    At -O3 this is what gcc generates for the lambda (code from godbolt)

    lambda:
        cmp DWORD PTR [rdi], 1
        sbb eax, eax
        and eax, -10
        add eax, 80
        ret
    

    This is a contrived and optimized way to express the following:

    • If a was a 0, the first comparison would set the carry flag CR. eax would actually be set to 32 1 values, and'ed with -10 (and that would yield -10 in eax) and then added 80 -> result is 70.

    • If a was something different from 0, the first comparison would not set the carry flag CR, eax would be set to zero, the and would have no effect and it would be added 80 -> result is 80.

    It has to be noted (thanks Marc Glisse) that if the function is marked as cold (i.e. unlikely to be called) gcc performs the right thing and optimizes the call away.

    MSVC generates more verbose code but the comparison isn't skipped.

    Clang is the only one which gets it right: the lambda hasn't its code optimized more than gcc did but it is not called

    mov edi, std::cout
    mov esi, 70
    call    std::basic_ostream >::operator<<(int)
    

    Morale: Clang seems to get it right but the optimization challenge is still open.

提交回复
热议问题