前两天在群里跟人讨论到写库时对于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. 63.1拓展之纯 CSS 创作一个摇摇晃晃的 loader

    效果地址:https://scrimba.com/c/cqKv4VCR HTML code: <div class="loader"> <span>Load ...

  2. linux文件属性的10个字符各代表什么意思

    10个字符表示文件类别和权限,具体如下: 例: 第一个字符表示文件类别,代表的含义如下:-:普通文件d:目录文件b:块设备文件c:字符设备文件l:符号链接文件 后面9个字符代表3组访问权限:第1组的3 ...

  3. python中__get__,__getattr__,__getattribute__的区别

    __get__,__getattr__和__getattribute都是访问属性的方法,但不太相同. object.__getattr__(self, name) 当一般位置找不到attribute的 ...

  4. 规模预算 之 FP法(作成中)

    五大要素 「外部入力」「外部出力」「内部論理ファイル」 「外部インタフェースファイル」「外部照会」 优点 1) 開発初期段階での概算が可能 2) エンドユーザが認識可能な計測法である(ユーザ目線での機 ...

  5. 550 5.7.1 Client does not have permissions to send as this sender

    收发邮件时出现以上这种情况,系统提示550 5.7.1 Client does not have permissions to send as this sender,这是什么原因赞成的呢? 活动目录 ...

  6. svg矢量图

    svg简介 Scalable Vector Graphics 可缩放矢量图形 SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失 svg知识点 svg如何绘图 svg和cnavas区别 svg ...

  7. 记账本,C,Github,service

    package service; import java.util.Collections; import java.util.List; import dao.CategoryDAO; import ...

  8. react mobx 装饰器语法配置

    1.弹出项目配置 npm run eject 此处注意,若弹出项目配置失败,请先执行以下两行代码(我的项目执行上一句都会报错,所以都会执行) 1.git add . 2.git commit -m & ...

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

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

  10. MySQL 5.7.9版本sql_mode=only_full_group_by

    这会导致select中只能出现group by后面出现的表的字段. 其实如果使用其他表的字段,聚合函数对无法对应其他表的字段. 建议放在子查询里. 如果想打破这个规则,可以设置sql_mode变量,将 ...