前两天在群里跟人讨论到写库时对于lambda和function的取舍,跑了写测试查了些资料后基本得出结论:

如果没有自由变量的情况下,一般不要用function。

如果有自由变量的话,C++中的lambda就是一个匿名类的实例,而如果没有的话,就是一个单纯的函数指针。为什么说尽量不要用function呢?我们来看一下下面的代码:

void lambdatest(vector<int> data)
{
sort(data.begin(), data.end(), [](int a, int b){ return a > b; });
sort(data.begin(), data.end(), [](int a, int b){ return a < b; });
} void functiontest(vector<int> data)
{
sort(data.begin(), data.end(), function<bool(int, int)>([](int a, int b){return a > b; }));
sort(data.begin(), data.end(), function<bool(int, int)>([](int a, int b){return a < b; }));
}

我们随机大概1000000个数据点,然后做升降序排列,调用sort对function和lambda表达式进行比较。

最后我们profile的结果如下:

最后那个qsorttest是调用了qsort函数。我们可以看到,function比lambda要慢几乎一倍以上,这个是release版,如果调成debug的话结果会更夸张一点,基本能到3~4倍,这是为什么呢?

这个要从C++模板开始说起了,首先我们都知道C++中的模板实例化是代码展开,就是指每次针对一个类型进行实例化都会将模板的代码拷贝一份,拷贝之后就可以对某一个类型进行特化,比如float可以用SSE指令等等,这个也包括这个实参有函数调用的话,可以进行内联。

优化的点就在内联上,也就是说,如果你想更快,就让模板对你的实参调用时准备一套专门的代码,然后内联。

那么我们怎么样能让内联起上作用呢?一般来讲,如果实参是一个函数指针,这个就没法内联了(其实也是可以的,不过这个我们在这里不讨论,跟优化实在关系不大),如果实参的函数是编译器绑定的,即stable name,我们就可以内联了。function这玩意儿本质上就是一个函数指针,运行的时候给他分配,所以不好内联;而lambda实际上是一个类型的实例,所以就可以。

我们现在来看编译器编译过程,编译器在编译同一个文件的时候,会为每个语义不同的lambda生成一个专门匿名类。在上面的测试代码中,sort函数会被分别实例化:

对于function,自然就是sort<int *, function<bool(int, int)>>。而lambda就是sort<int *, lambda_greater> 和 sort<int *, lambda_less>,lambda被实例化成了两份代码,但是在编译时编译器可以针对不同的匿名类做去特化,去内联。但是function就不行了,就只被实例化了一份代码,每次调用时都会调用,只是传入的函数指针不同,没法内联从而优化。

上面这段话其实可以通过profile来证实:

我们看到function调用了两次,而lambda因为内联只用了一次匿名类。

其实这个在C++98里一样,自己写functor或者std::less的时候效率高,但是一旦用的function时效率立刻下来了。这个就是C++模板里面inline的优势。

就是这样。其实很多东西最后讨论下来,没有太必要纠结这个,因为很多底层的优化并不是我们程序员考虑的,学习的很多东西都是很快会过时的,编译稍微加个参数立马就GG,所以大概明白一些基本的概念,然后在具体的实现时多profile,通过profile来推导编译的优化可能,从而来进行代码优化。

有关C++模板inline的高性能在lambda与function的体现的更多相关文章

  1. C++11的闭包(lambda、function、bind)

    c++11开始支持闭包,闭包:与函数A调用函数B相比较,闭包中函数A调用函数B,可以不通过函数A给函数B传递函数参数,而使函数B可以访问函数A的上下文环境才可见(函数A可直接访问到)的变量:比如: 函 ...

  2. linux下, 再次遇到使用thinkphp的模板标签时,报错used undefined function \Think\Template\simplexml_load_string() 是因为没有安装 php-xml包

    linux下, 使用thinkphp的模板标签,如 eq, gt, volist defined, present , empty等 标签时, 报错: used undefined function ...

  3. Mindjet MindManager 2012 从模板创建出现“Runtime Error pure virtual function call” 解决方法

    我的Mindjet MindManager 2012 Pro也就是MindManager10 在应用模板之后总会显示 Microsoft Visual C++ Runtime Library Runt ...

  4. Java Lambda基础——Function, Consumer, Predicate, Supplier, 及FunctionalInterface接口

    这几个接口经常与Lambda结合使用,网上当然也有很多介绍,不过有些过于繁琐,有些又偏简单,秉着实用主义精神,今天这里折中一下,把介绍的内容分为两部分,第一部分相当于TLDR,总结几个"口诀 ...

  5. C++ 使用Lambda

    基础使用: C++中的Lambda表达式详解 c++11的闭包(lambda.function.bind) C++ lambda作为函数参数,实现通用的查找接口 C++11系列-lambda函数 进阶 ...

  6. 高性能双端js模板

    高性能双端js模板(新增filter)---simplite simplite是一款js实现的模板引擎,它能够完成浏览器端js模版和node服务器端js模板的数据渲染. 渲染性能十分突出. 支持浏览器 ...

  7. C++ lambda的演化

    翻译自https://www.bfilipek.com/2019/02/lambdas-story-part1.html与https://www.bfilipek.com/2019/02/lambda ...

  8. C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

    模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模 ...

  9. c++一些语法模板

    函数模板特 template <class T> int compare(T v1,T v2) { if(v1<v2) return -1; else if(v1>v2) re ...

随机推荐

  1. Http协议和Https协议的安全性问题

    https://www.cnblogs.com/intsmaze/p/6009648.html https://blog.csdn.net/jeffleo/article/details/768630 ...

  2. js 模拟css3 动画3

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 如何编写Window服务程序(C# )

    虚拟需求:编写一个Window服务,并注册到操作系统的服务里.让他隔30秒运行一下(写当前日期到一个文本里) 步骤: 创建一个Window 窗体应用程序项目(Greatwall.Mes.Windows ...

  4. mysql学习笔记--数据操作

    一.插入数据 1. 语法:insert into 表名 (字段名.字段名,...) values (值1,值2...) 2. 注意: a. 插入字段的个数和顺序与值的个数和顺序必须一致 b. 通过de ...

  5. python中assert详解

    assert基础 官方解释:"Assert statements are a convenient way to insert debugging assertions into a pro ...

  6. 169. Majority Element (Array)

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  7. matlab中mat文件简单存/取

    >>abc=[,,,,,]; >>save data save file_name:命令可以将当前项目中变量的值保存到file_name中去,这里的data文件就是mat文件. ...

  8. related_name和related_query_name举例区别

    例1: class UserInfo(models.Model): nickname = models.CharField(max_length=32) username = models.CharF ...

  9. Python开发——数据类型【字符串】

    字符串定义 字符串是一个有序的字符的集合,用于存储和表示基本的文本信息 在Python中加了引号的字符,都被认为是字符串! 单引号.双引号.多引号之间的区别? 答案:单双引号没有区别 多引号的作用? ...

  10. 判断是否是json

    转:https://blog.csdn.net/dy_smile/article/details/46739251 function isJson(obj) { var isjson = typeof ...