C ++编译器是否对lambda关闭执行编译时优化? [英] Do C++ compilers perform compile-time optimizations on lambda closures?

查看:247
本文介绍了C ++编译器是否对lambda关闭执行编译时优化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  const int a = 0; 
int c = 0;
for(int b = 0; b <10000000; b ++)
{
if(a)c ++;
c + = 7;





变量'a'等于零,因此编译器可以在编译时推断,指令'if(a)c ++;'将永远不会被执行,并将优化它。 我的问题: lambda关闭是否也发生了同样的情况?



查看另一段代码:

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

编译器是否知道'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;
};
}

函数< int()> a_is_zero = generate_lambda(0);
函数< int()> a_is_one = generate_lambda(1);

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



gcc或llvm是否有这种优化?



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

查看由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();
}

编译为一些样板文件和

  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

这是您希望看到优化的循环。 (直播)但是,如果你实际使用lambda(而不是 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();

$ / code>

编译为

  movl $ 70000000,%eax 
ret

即该循环被完全删除。 (直播



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

正如@MarcGlisse正确指出的那样,即使使用 std :: function ,clang3.6也会执行期望的优化(相当于上面的第二种情况) / code>。 (直播



再次感谢@MarkGlisse:如果包含 std :: function 的函数不是,那么称为 main ,使用gcc5.2进行的优化在gcc + main和clang之间,也就是说函数被减少到返回70000000; 加上一些额外的代码。 (直播



Bonus编辑2,这次是我的:如果你使用-O3,gcc会(出于某种原因),如 Marco的答案,优化 std :: function

  cmpl $ 1,(%rdi)
sbbl%eax,%eax
andl $ -10000000,%eax
addl $ 80000000,%eax
ret

,并保留其余部分,如 not_main 的情况。所以我想在行的底部,只需要使用 std :: function 来衡量。


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;
}

Variable 'a' equals zero, so the compiler can deduce on compile time, that the instruction 'if(a) c++;' will never be executed and will optimize it away.

My question: Does the same happen with lambda closures?

Check out another piece of code:

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;
}

Will the compiler know that 'a' is 0 and will it optimize the lambda?

Even more sophisticated example:

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);

Will the compiler be smart enough to optimize the first lambda when it knows that 'a' is 0 at generation time?

Does gcc or llvm have this kind of optimizations?

I'm asking because I wonder if I should make such optimizations manually when I know that certain assumptions are satisfied on lambda generation time or the compiler will do that for me.

解决方案

Looking at the assembly generated by gcc5.2 -O2 shows that the optimization does not happen when using 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();
}

compiles to some boilerplate and

    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

which is the loop you wanted to see optimized away. (Live) But if you actually use a lambda (and not an std::function), the optimization does happen:

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();
}

compiles to

movl    $70000000, %eax
ret

i.e. the loop was removed completely. (Live)

Afaik, you can expect a lambda to have zero overhead, but std::function is different and comes with a cost (at least at the current state of the optimizers, although people apparently work on this), even if the code "inside the std::function" would have been optimized. (Take that with a grain of salt and try if in doubt, since this will probably vary between compilers and versions. std::functions overhead can certainly be optimized away.)

As @MarcGlisse correctly pointed out, clang3.6 performs the desired optimization (equivalent to the second case above) even with std::function. (Live)

Bonus edit, thanks to @MarkGlisse again: If the function that contains the std::function is not called main, the optimization happening with gcc5.2 is somewhere between gcc+main and clang, i.e. the function gets reduced to return 70000000; plus some extra code. (Live)

Bonus edit 2, this time mine: If you use -O3, gcc will, (for some reason) as explained in Marco's answer, optimize the std::function to

cmpl    $1, (%rdi)
sbbl    %eax, %eax
andl    $-10000000, %eax
addl    $80000000, %eax
ret

and keep the rest as in the not_main case. So I guess at the bottom of the line, one will just have to measure when using std::function.

这篇关于C ++编译器是否对lambda关闭执行编译时优化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆