C++编译器是否对lambda闭包执行编译时优化?

haa*_*ael 10 c++ optimization lambda gcc llvm-clang

假设我们有以下(无意义)代码:

const int a = 0;
int c = 0;
for(int b = 0; b < 10000000; b++)
{
    if(a) c++;
    c += 7;
}
Run Code Online (Sandbox Code Playgroud)

变量'a'等于零,因此编译器可以在编译时推断出指令'if(a)c ++;' 将永远不会被执行,并将优化它.

我的问题:lambda闭包会发生同样的情况吗?

查看另一段代码:

const int a = 0;
function<int()> lambda = [a]()
{
    int c = 0;
    for(int b = 0; b < 10000000; b++)
    {
        if(a) c++;
        c += 7;
    }
    return c;
}
Run Code Online (Sandbox Code Playgroud)

编译器是否会知道'a'是0并且它会优化lambda吗?

更复杂的例子:

function<int()> generate_lambda(const int a)
{
    return [a]()
    {
        int c = 0;
        for(int b = 0; b < 10000000; b++)
        {
            if(a) c++;
            c += 7;
        }
        return c;
    };
}

function<int()> a_is_zero = generate_lambda(0);
function<int()> a_is_one = generate_lambda(1);
Run Code Online (Sandbox Code Playgroud)

当编译器在生成时知道'a'为0时,编译器是否足够聪明以优化第一个lambda?

gcc或llvm有这种优化吗?

我问,因为当我知道在lambda生成时间内满足某些假设或者编译器会为我这样做时,我想知道是否应该手动进行这样的优化.

Bau*_*gen 12

查看gcc5.2 -O2生成的程序集显示使用时不会发生优化std::function:

#include <functional>

int main()
{
    const int a = 0;    
    std::function<int()> lambda = [a]()
    {
        int c = 0;
        for(int b = 0; b < 10000000; b++)
        {
            if(a) c++;
            c += 7;
        }
        return c;
    };

    return lambda();
}
Run Code Online (Sandbox Code Playgroud)

编译到一些样板和

    movl    (%rdi), %ecx
    movl    $10000000, %edx
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L3:
    cmpl    $1, %ecx
    sbbl    $-1, %eax
    addl    $7, %eax
    subl    $1, %edx
    jne .L3
    rep; ret
Run Code Online (Sandbox Code Playgroud)

这是你希望看到优化的循环.(实时)但是如果你实际使用lambda(而不是an std::function),优化确实会发生:

int main()
{
    const int a = 0;    
    auto lambda = [a]()
    {
        int c = 0;
        for(int b = 0; b < 10000000; b++)
        {
            if(a) c++;
            c += 7;
        }
        return c;
    };

    return lambda();
}
Run Code Online (Sandbox Code Playgroud)

编译成

movl    $70000000, %eax
ret
Run Code Online (Sandbox Code Playgroud)

即完全去除循环.(现场)

Afaik,你可以期待一个lambda零开销,但是std::function不同并且带有成本(至少在优化器的当前状态下,尽管人们显然在这方面工作),即使代码"内部std::function"本来是优化.(带上一粒盐然后尝试,如果有疑问,因为这可能会在编译器和版本之间有所不同.std::function开销肯定会被优化掉.)

正如@MarcGlisse正确指出的那样,clang3.6执行了所需的优化(相当于上面的第二种情况),即使是std::function.(现场)

奖励编辑,再次感谢@MarkGlisse:如果没有调用包含该函数的函数,std::function则gcc5.2发生的优化介于gcc + main和clang之间,即函数减少到加上一些额外的代码.(现场)mainreturn 70000000;

奖励编辑2,这次我的:如果你使用-O3,gcc将(由于某种原因)Marco的答案中解释,优化std::function

cmpl    $1, (%rdi)
sbbl    %eax, %eax
andl    $-10000000, %eax
addl    $80000000, %eax
ret
Run Code Online (Sandbox Code Playgroud)

并保持其余的not_main情况.所以我想在线的底部,人们只需要在使用时进行测量std::function.

  • @bitticker`std :: function`*是*语言的一部分:编译器根本不需要将它实现为C++代码.大多数人都这样做,因为它更容易做对.老实说,困难的部分是你可以将底层对象从`std :: function`中取回,因此优化必须在执行"丢弃所有内容"之前审核这种可能性. (4认同)
  • 请注意,clang甚至优化了std :: function的情况. (2认同)
  • 有趣的是,如果你将`main`重命名为`not_main`(所以函数没有被标记为冷,这会禁用一些优化),gcc设法理解`return 70000000;`但它在此之前保留了一些无用的代码. .. (2认同)