Kon*_*app 3 c++ performance lambda
我想知道我的编译器使用以下代码做了什么
void design_grid::design_valid()
{
auto valid_idx = [this]() {
if ((row_num < 0) || (col_num < 0))
{
return false;
}
if ((row_num >= this->num_rows) || (col_num >= this->num_rows))
{
return false;
}
return true;
}
/* some code that calls lambda function valid_idx() */
}
Run Code Online (Sandbox Code Playgroud)
如果我反复调用上面的(design_grid::design_valid)成员函数函数,那么当我的程序遇到valid_idx每次创建时会发生什么?编译器是否在编译时内联后面调用的代码,以便在valid_idx遇到创建时实际上不执行任何操作?
UPDATE
汇编代码的一部分如下.如果这有点太多了,我会稍后发布另一批有色的代码,以说明哪些部分是哪些.(目前没有一种很好的方法可以与我对代码段进行着色).还要注意我已经更新了我的成员函数的定义和上面的lambda函数,以反映它在我的代码中真正命名的内容(因此,在汇编语言中).
在任何情况下,似乎lambda都是与main函数分开定义的.lambda函数由_ZZN11design_grid12design_validEvENKUliiE_clEii下面的函数表示.反过来,在函数的正下方,外部函数(design_grid::design_valid)由_ZN11design_grid12design_validEv开始表示.稍后_ZN11design_grid12design_validEv,打电话给_ZZN11design_grid12design_validEvENKUliiE_clEii.进行调用的这一行看起来像
call _ZZN11design_grid12design_validEvENKUliiE_clEii #
Run Code Online (Sandbox Code Playgroud)
如果我错了,请纠正我,但这意味着编译器将lambda定义为函数外的正常design_valid函数,然后将其作为正常函数调用它应该是什么时候?也就是说,每次遇到声明lambda函数的语句时,它都不会创建新对象?我在该特定位置看到lambda函数的唯一迹线是# tmp85, valid_idx.__this在第二个函数中注释的行,就在函数开始时重新调整的基本和堆栈指针之后,但这只是一个简单的movq操作.
.type _ZZN11design_grid12design_validEvENKUliiE_clEii, @function
_ZZN11design_grid12design_validEvENKUliiE_clEii:
.LFB4029:
.cfi_startproc
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp) # __closure, __closure
movl %esi, -12(%rbp) # row_num, row_num
movl %edx, -16(%rbp) # col_num, col_num
cmpl $0, -12(%rbp) #, row_num
js .L107 #,
cmpl $0, -16(%rbp) #, col_num
jns .L108 #,
.L107:
movl $0, %eax #, D.81546
jmp .L109 #
.L108:
movq -8(%rbp), %rax # __closure, tmp65
movq (%rax), %rax # __closure_4(D)->__this, D.81547
movl 68(%rax), %eax # _5->D.69795.num_rows, D.81548
cmpl -12(%rbp), %eax # row_num, D.81548
jle .L110 #,
movq -8(%rbp), %rax # __closure, tmp66
movq (%rax), %rax # __closure_4(D)->__this, D.81547
movl 68(%rax), %eax # _7->D.69795.num_rows, D.81548
cmpl -16(%rbp), %eax # col_num, D.81548
jg .L111 #,
.L110:
movl $0, %eax #, D.81546
jmp .L109 #
.L111:
movl $1, %eax #, D.81546
.L109:
popq %rbp #
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE4029:
.size _ZZN11design_grid12design_validEvENKUliiE_clEii,.-_ZZN11design_grid12design_validEvENKUliiE_clEii
.align 2
.globl _ZN11design_grid12design_validEv
.type _ZN11design_grid12design_validEv, @function
_ZN11design_grid12design_validEv:
.LFB4028:
.cfi_startproc
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
pushq %rbx #
subq $72, %rsp #,
.cfi_offset 3, -24
movq %rdi, -72(%rbp) # this, this
movq -72(%rbp), %rax # this, tmp85
movq %rax, -32(%rbp) # tmp85, valid_idx.__this
movl $0, -52(%rbp) #, active_count
movl $0, -48(%rbp) #, row_num
jmp .L113 #
.L128:
movl $0, -44(%rbp) #, col_num
jmp .L114 #
.L127:
movl -44(%rbp), %eax # col_num, tmp86
movslq %eax, %rbx # tmp86, D.81551
Run Code Online (Sandbox Code Playgroud)
闭包(未命名的函数对象)是lambda,因为对象是类.这意味着lambda_func从lambda重复创建一个闭包:
[this]() {
/* some code here */
}
Run Code Online (Sandbox Code Playgroud)
就像一个对象可以从一个类重复创建一样.当然,编译器可以优化一些步骤.
至于这部分问题:
编译器是否在编译时内联后面调用的代码,以便在
lambda_func遇到创建时实际上不执行任何操作 ?
看到:
这是一个测试可能发生的事件的示例程序:
#include <iostream>
#include <random>
#include <algorithm>
class ClassA {
public:
void repeatedly_called();
private:
std::random_device rd{};
std::mt19937 mt{rd()};
std::uniform_int_distribution<> ud{0,10};
};
void ClassA::repeatedly_called()
{
auto lambda_func = [this]() {
/* some code here */
return ud(mt);
};
/* some code that calls lambda_func() */
std::cout << lambda_func()*lambda_func() << '\n';
};
int main()
{
ClassA class_a{};
for(size_t i{0}; i < 100; ++i) {
class_a.repeatedly_called();
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到,在这种特殊情况下,函数repeatedly_called不会调用lambda(生成随机数),因为它已被内联:
在问题的更新中,似乎没有内联lambda指令.从理论上讲,闭包是创建的,通常意味着一些内存分配,但是,编译器可能会优化并删除一些步骤.
只有thislambda 的捕获类似于成员函数.
基本上发生的是编译器使用函数调用操作符创建一个未命名的类,将捕获的变量作为成员变量存储在类中.然后编译器使用这个未命名的类来创建一个对象,这是你的lambda_func变量.