C++ C++;函数调用lambda obj,按值调用比按引用调用快

C++ C++;函数调用lambda obj,按值调用比按引用调用快,c++,lambda,C++,Lambda,测试代码是这样的,计时器将输出销毁时经过的时间 uint64_t res1 = 0, res2 = 0; void test_accumulate_bind_function(uint64_t& x, uint64_t i) { x += i; } uint64_t res1 = 0, res2 = 0, res3 = 0, res4=0; template <typename Function> vo

测试代码是这样的,计时器将输出销毁时经过的时间

    uint64_t res1 = 0, res2 = 0;
    void test_accumulate_bind_function(uint64_t& x, uint64_t i)
    {
         x += i;
    }
    uint64_t res1 = 0, res2 = 0, res3 = 0, res4=0;
    template <typename Function>
    void do_loop_ref(Function & func, const uint64_t upper_limit = 100000)
    {
        for (uint64_t i = 0; i < upper_limit; ++i)
            func(i);
    }

    template <typename Function>
    void do_loop_forward(Function && func, const uint64_t upper_limit = 100000)
    {
        Function f(std::forward<Function>(func));
        for (uint64_t i = 0; i < upper_limit; ++i)
            f(i);
    }

    template <typename Function>
    void do_loop_copy(Function func, const uint64_t upper_limit = 100000)
    {
        for (uint64_t i = 0; i < upper_limit; ++i)
            func(i);
    }

    void test_bind_copy()
    {
        {
            namespace arg = std::placeholders;
            uint64_t x = 0;
            auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
            std::cout << "reference:";
            timer t;
            do_loop_ref(accumulator);
            res1 = x;
        }
        {
            namespace arg = std::placeholders;
            uint64_t x = 0;
            auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
            std::cout << "copy:";
            timer t;
            do_loop_copy(accumulator);
            res2 = x;
        }
        {
            namespace arg = std::placeholders;
            uint64_t x = 0;
            auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
            std::cout << "localcopy:";
            timer t;
            do_loop_forward(accumulator);
            res3 = x;
        }
        {
            namespace arg = std::placeholders;
            uint64_t x = 0;
            auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
            std::cout << "move:";
            timer t;
            do_loop_forward(std::move(accumulator));
            res4 = x;
        }
        printf("res1:%lld, res2:%lld, res3:%lld, res4:%lld\n", res1, res2, res3, res4);
    }

    void test_copy()
    {
        {
            uint64_t x = 0;
            auto accumulator = [&x](uint64_t i){ return x += i; };
            std::cout << "reference:";
            timer t;
            do_loop_ref(accumulator);
            res1 = x;
        }
        {
            uint64_t x = 0;
            auto accumulator = [&x](uint64_t i){ return x += i; };
            std::cout << "copy:";
            timer t;
            do_loop_copy(accumulator);
            res2 = x;
        }
        {
            uint64_t x = 0;
            auto accumulator = [&x](uint64_t i){ return x += i; };
            std::cout << "localcopy:";
            timer t;
            do_loop_forward(accumulator);
            res3 = x;
        }
        {
            uint64_t x = 0;
            auto accumulator = [&x](uint64_t i){ return x += i; };
            std::cout << "move:";
            timer t;
            do_loop_forward(std::move(accumulator));
            res4 = x;
        }

        printf("res1:%lld, res2:%lld, res3:%lld, res4:%lld\n", res1, res2, res3, res4);
    }

int main()
{
    test_copy();
    test_bind_copy();
}
uint64\u t res1=0,res2=0;
无效测试累积绑定函数(uint64\U t&x,uint64\U t i)
{
x+=i;
}
uint64_t res1=0,res2=0,res3=0,res4=0;
模板
无效循环参考(函数和函数,常数64上限=100000)
{
对于(uint64_t i=0;i<上限;++i)
func(i);
}
模板
无效循环前进(函数和函数,常数64上限=100000)
{
函数f(std::forward(func));
对于(uint64_t i=0;i<上限;++i)
f(i);
}
模板
无效循环复制(函数func,常数64上限=100000)
{
对于(uint64_t i=0;i<上限;++i)
func(i);
}
无效测试绑定副本()
{
{
名称空间arg=std::占位符;
uint64_t x=0;
自动累加器=std::bind(&test\u accumulate\u bind\u函数,std::ref(x),arg::\u 1);

std::cout您是否在编译优化打开的情况下运行基准测试? 在gcc中使用
-O3
进行编译时,这两个代码在汇编中看起来完全相同(因此应该执行完全相同的操作)

在没有任何优化标志的情况下,代码看起来还是一样的,只是在通过引用传递时有一个额外的间接级别

 // pass by reference

 mov     rax, QWORD PTR [rbp-24]                

// pass by value
lea     rax, [rbp-32]  

std::function
在内部只是包装函数内部状态的结构(lambda中捕获的变量)。通过引用传递
函数
可以想象为传递一个指向结构的指针,其中包含该函数捕获的所有状态。因此,调用方和被调用方在同一内存位置上工作。当通过值传递时,调用方和被调用方具有独立的副本,并且一个副本上的写入和读取不会影响另一个副本

在上述情况下,当函数按值传递时,地址按原样加载,而当其按引用传递时,在调用lambda之前会读取额外的内存。检查


额外的间接级别可能会发挥诸如引用位置之类的作用,但这将与执行此操作的硬件紧密耦合。但这在很大程度上取决于如何编译此代码、在何处执行代码以及如何度量它(您尚未共享
计时器的代码).因此,由于问题中的信息有限,我会将额外的阅读时间归咎于此。

我添加了两个测试用例,但结果令人困惑,传递值与移动到本地f相同,传递引用与复制到本地f相同“std::function在内部只是包装函数内部状态的结构(lambda情况下捕获的变量)”不是真的,不是。
std::function
是一个类型擦除器,而不是包装器,因此不知道存储函数的类型,更不用说它的状态了(例如,如您所述捕获的lambda变量).@bolov同意。但我不想偏离这个问题。类型擦除需要更长的脚注。我想强调的是,捕获的状态将随函数在函子对象中移动。另外,std::function是否使用类型擦除或其他魔法由实现来决定。@bashrc请放弃请原谅我吹毛求疵,但是根据定义,
std::function
是一个类型擦除类。一个函数指针,一个定义了
operator()
的类型,一个没有捕获的lambda或一个有捕获的lambda(都接受2个int参数并返回一个int)-所有存储到同一类型:
std::function
。这是类型擦除。有关原始类型的信息丢失。用于实现这一点的类型擦除技术确实留给了实现(很可能是
void*
+多态性的一种形式)。