c++ - 重复执行 lambda 函数

标签 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() */
}

如果我重复调用上面的类成员函数(design_grid::design_valid),那么我的程序每次遇到valid_idx的创建时到底发生了什么?编译器是否在编译时内联稍后调用它的代码,以便在遇到创建 valid_idx 时它实际上不执行任何操作?

更新

一段汇编代码如下。如果这有点太多太多了,我稍后会发布另一批代码,这些代码是彩色的,以说明哪些部分是哪些部分。 (目前没有很好的方法来为我的代码段着色)。另请注意,我已经更新了我的成员函数和上面的 lambda 函数的定义,以反射(reflect)它在我的代码中的真实命名(因此,在汇编语言中)。

无论如何,看来 lambda 是与 main 函数分开定义的。 lambda 函数由正下方的 _ZZN11design_grid12design_validEvENKUliiE_clEii 函数表示。在该函数的正下方,由 _ZN11design_grid12design_validEv 表示的外部函数 (design_grid::design_valid) 开始。稍后在 _ZN11design_grid12design_validEv 中调用 _ZZN11design_grid12design_validEvENKUliiE_clEii。进行调用的这一行看起来像

call    _ZZN11design_grid12design_validEvENKUliiE_clEii #

如果我错了请纠正我,但这意味着编译器将 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

最佳答案

闭包 ( unnamed function objects ) 对于 lambda 就像对象对于类一样。这意味着闭包 lambda_func 是从 lambda 重复创建的:

[this]() {
        /* some code here */
    }

就像可以从类中重复创建对象一样。当然编译器可能optimize几步之遥。

关于这部分问题:

Does the compiler inline the code where it is called later, at compile time, so that it does not actually do anything where the creation of lambda_func is encountered?

参见:

下面是一个示例程序,用于测试可能发生的情况:

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

已测试here .

我们可以看到,在这种特殊情况下,函数 repeatedly_called 不会调用 lambda(它正在生成随机数),因为它已被内联:

enter image description here enter image description here

在问题的更新中,似乎没有内联 lambda 指令。理论上闭包被创建,通常这意味着一些内存分配,但是,编译器可能会优化并删除一些步骤。

只有 this 的捕获,lambda 类似于成员函数。

关于c++ - 重复执行 lambda 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40153725/

相关文章:

c++ - if/else if 总是去 else 语句

c++ - NCurses 的背景颜色数量似乎有限(前景色效果很好)

c - 循环内的 strcat() 与 sprintf()

c# - C#异步服务器套接字的奇怪性能

c++ - NRVO 不应该保证本地命名变量和调用站点变量采用相同的地址吗?

c++ - 变量类派生自某个抽象类的类模板

scala - Spark 数与拍摄和长度

lambda - 如何使用lambda过滤具有二级列表中对象属性的列表?

python - 在 Pandas 的列上应用 lambda

c# - 让方法语法中的查询语法?