C++中labmda效率问题

最近使用lambda进行替换原有逻辑,不经想到使用lambda与直接写逻辑究竟有什么区别。当然在不开优化下lambda会变成匿名函数,但是在开了O2优化的情况是否还是一样呢?是否lambda也是会进行内联。

本文各种汇编代码皆是在开启O2优化的情况下生成,使用的是VS2019。

最简单的labmda

首先我我们先看一下最简单lambda。

// main.cpp

int main()
{
	using namespace std;
	auto testf = []() {
		for (int i = 0; i < 10; ++i)
		{
			if (i % 2 == 0)
				cout << "hi";
		}
	};
	testf();
}

查看反汇编代码,这里是testf()的反汇编

00007FF6DB7E1246  xor         ebx,ebx  
00007FF6DB7E1248  test        bl,1  
00007FF6DB7E124B  jne         main+20h (07FF6DB7E1260h)  
00007FF6DB7E124D  mov         rcx,qword ptr [__imp_std::cout (07FF6DB7E20B0h)]  
00007FF6DB7E1254  lea         rdx,[string "hi" (07FF6DB7E2284h)]  
00007FF6DB7E125B  call        std::operator<<<std::char_traits<char> > (07FF6DB7E1000h)  
00007FF6DB7E1260  inc         ebx  
00007FF6DB7E1262  cmp         ebx,0Ah  
00007FF6DB7E1265  jl          main+8h (07FF6DB7E1248h)

发现在O2的优化下直接就内联了,也没有产生函数调用,第6行的call是调用cout的。

类中的lambda

类A的声明与定义

// A.h
#pragma once
#include <iostream>
#include<string>
//#define VIRTUAL
//#define NOINLINE
#ifdef VIRTUAL
class Base {
public:
	virtual void f(){
		std::cout << "Base";
	};
};
#endif 
class A
#ifdef VIRTUAL
	: public Base
#endif
{
public:
	A(int a, std::string b) :parm1(a), parm2(b) {};
	A(int a) :parm1(a) {};
#ifdef NOINLINE
	__declspec(noinline)
#endif
#ifdef VIRTUAL
		virtual
#endif 
	void f();
	int parm1;
	std::string parm2;
};

f函数的定义

// A.cpp
#include"A.h"
void A::f()
{
	int s_t = 1;
	auto t = [&s_t, this]() {
		for (int i = 0; i < 10; ++i)
		{
			if (i % 2 == 0)
				std::cout << "hi";
		}
		parm1 += parm2.size();
#ifdef VIRTUAL
		Base::f();
#else
		f();
#endif 
	};
	t();
}

main文件

//main.cpp
#include "A.h"
extern class Base;
extern class A;
int main()
{
	using namespace std;
	
	A a(1, "hi");
#ifdef VIRTUAL
	a.f();
	Base* t = &a;
	t->f();
	std::cout << a.parm1 << a.parm2;
#else
	a.f();
#endif // VURTUAL
}

VIRTUAL和ONINLINE使用宏控制使用虚函数时的情况与强制直接内联的情况。
首先我们不定义ONINLINE和VIRTUAL宏,来看一下a.f()的反汇编代码

00007FF7283E1557  test        bl,1  
00007FF7283E155A  jne         main+178h (07FF7283E1568h)  
00007FF7283E155C  mov         rcx,qword ptr [__imp_std::cout (07FF7283E3080h)]  
00007FF7283E1563  call        std::operator<<<std::char_traits<char> > (07FF7283E1000h)  
00007FF7283E1568  inc         ebx  
00007FF7283E156A  cmp         ebx,0Ah  
00007FF7283E156D  jl          main+167h (07FF7283E1557h)  

可以发现f()函数直接就被优化了,函数调用都被优化没了。这样子我我们无法测试类内函数使用lambda的情况,所以我定义对了ONINLINE宏,使编译器强制不内联f()函数。
让我们看看定义了ONINLINE的反汇编吧。

00007FF7B39C1599  lea         rcx,[rbp-38h]  
00007FF7B39C159D  call        A::f (07FF7B39C1000h)  
00007FF7B39C15A2  nop  

可以看到确实不内联了,我们进函数内看一看

void A::f()
{
00007FF7B39C1000  mov         qword ptr [rsp+8],rbx  
00007FF7B39C1005  push        rdi  
00007FF7B39C1006  sub         rsp,20h  
00007FF7B39C100A  mov         rdi,rcx  
	int s_t = 1;
	auto t = [&s_t, this]() {
		for (int i = 0; i < 10; ++i)
		{
			if (i % 2 == 0)
				std::cout << "hi";
		}
		parm1 += parm2.size();
#ifdef VIRTUAL
		Base::f();
#else
		//f();
#endif 
	};
00007FF7B39C100D  xor         ebx,ebx  
00007FF7B39C100F  nop  
	t();
00007FF7B39C1010  test        bl,1  
00007FF7B39C1013  jne         A::f+21h (07FF7B39C1021h)  
00007FF7B39C1015  mov         rcx,qword ptr [__imp_std::cout (07FF7B39C3080h)]  
00007FF7B39C101C  call        std::operator<<<std::char_traits<char> > (07FF7B39C1040h)  
00007FF7B39C1021  inc         ebx  
00007FF7B39C1023  cmp         ebx,0Ah  
00007FF7B39C1026  jl          A::f+10h (07FF7B39C1010h)  
00007FF7B39C1028  mov         eax,dword ptr [rdi+18h]  
00007FF7B39C102B  add         dword ptr [rdi],eax  
}

可以发现lambda又被优化掉了。

虚函数中的lambda

接着我们定义VIRTUAL宏,不定义NOINLINE宏,反汇编看一下

#ifdef VIRTUAL
	a.f();
00007FF6C5EB15D4  test        bl,1  
00007FF6C5EB15D7  jne         main+18Ch (07FF6C5EB15ECh)  
00007FF6C5EB15D9  lea         rdx,[string "hi" (07FF6C5EB3358h)]  
00007FF6C5EB15E0  mov         rcx,qword ptr [__imp_std::cout (07FF6C5EB3088h)]  
00007FF6C5EB15E7  call        std::operator<<<std::char_traits<char> > (07FF6C5EB1060h)  
00007FF6C5EB15EC  inc         ebx  
00007FF6C5EB15EE  cmp         ebx,0Ah  
00007FF6C5EB15F1  jl          main+174h (07FF6C5EB15D4h)  
00007FF6C5EB15F3  mov         eax,dword ptr [rbp-18h]  
00007FF6C5EB15F6  add         dword ptr [rbp-30h],eax  
00007FF6C5EB15F9  lea         rdx,[string "Base" (07FF6C5EB3350h)]  
00007FF6C5EB1600  mov         rcx,qword ptr [__imp_std::cout (07FF6C5EB3088h)]  
00007FF6C5EB1607  call        std::operator<<<std::char_traits<char> > (07FF6C5EB1060h)  
	Base* t = &a;
	t->f();
00007FF6C5EB160C  lea         rcx,[rbp-38h]  
00007FF6C5EB1610  mov         rax,qword ptr [rbp-38h]  
00007FF6C5EB1614  call        qword ptr [rax]  

可以发现a.f()这种自身调用直接内联优化了,而多态转换后的无法内联优化,我们进去看一下lambda是否还会被内联优化。

void A::f()
{
00007FF6C5EB1000  mov         qword ptr [rsp+8],rbx  
00007FF6C5EB1005  push        rdi  
00007FF6C5EB1006  sub         rsp,20h  
00007FF6C5EB100A  mov         rdi,rcx  
	int s_t = 1;
	auto t = [&s_t, this]() {
		for (int i = 0; i < 10; ++i)
		{
			if (i % 2 == 0)
				std::cout << "hi";
		}
		parm1 += parm2.size();
#ifdef VIRTUAL
		Base::f();
#else
		//f();
#endif 
	};
00007FF6C5EB100D  xor         ebx,ebx  
00007FF6C5EB100F  nop  
	t();
00007FF6C5EB1010  test        bl,1  
00007FF6C5EB1013  jne         A::f+28h (07FF6C5EB1028h)  
00007FF6C5EB1015  mov         rcx,qword ptr [__imp_std::cout (07FF6C5EB3088h)]  
00007FF6C5EB101C  lea         rdx,[string "hi" (07FF6C5EB3358h)]  
00007FF6C5EB1023  call        std::operator<<<std::char_traits<char> > (07FF6C5EB1060h)  
00007FF6C5EB1028  inc         ebx  
00007FF6C5EB102A  cmp         ebx,0Ah  
00007FF6C5EB102D  jl          A::f+10h (07FF6C5EB1010h)  
00007FF6C5EB102F  mov         eax,dword ptr [rdi+20h]  
00007FF6C5EB1032  lea         rdx,[string "Base" (07FF6C5EB3350h)]  
00007FF6C5EB1039  add         dword ptr [rdi+8],eax  
00007FF6C5EB103C  mov         rcx,qword ptr [__imp_std::cout (07FF6C5EB3088h)]  
}

可以看到不仅仅时lambda被优化,甚至父类函数也被优化了。

总结

可以发现在不涉及多态的情况下,编译器能在编译期就能确定具体的调用函数,那么这个函数就可能会被内联。就算是lambda也不例外。

所以一般情况下使用lambda的开销几乎为0,因为编译器会做内联优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值