前两天在群里跟人讨论到写库时对于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. python字符串前面的r/u/b的意义 (笔记)

    u/U:表示unicode字符串 : 不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unicode编码. r/R:非转义的原始字符串: 与普通字符相比,其他相对特殊的字符,其中可能包含 ...

  2. 【HQL】小技巧

    case1.a与b匹配表保留一条匹配关系 背景:匹配b,b匹配a在同一张表: match_table表为: uid,m_uid 111,222 222,111 需求:只保留一条匹配关系. 结果为: u ...

  3. Python正则替换字符串函数re.sub用法示例(1)

    本文实例讲述了Python正则替换字符串函数re.sub用法.分享给大家供大家参考,具体如下: python re.sub属于python正则的标准库,主要是的功能是用正则匹配要替换的字符串然后把它替 ...

  4. 从performance_schema中查看MySQL活动Session的详细执行信息

    本文出处:http://www.cnblogs.com/wy123/p/7851294.html 在做数据库的异常诊断的时候,之前在SQL Server上的时候,最主要的参考信息之一就是去看当前的活动 ...

  5. Linux:sudo,没有找到有效的 sudoers 资源。

    首先,这是因为用户的权限不够导致的. 使用 ls -l /etc/passwd 查看所有用户及权限.只有可读权限(r),说明用户的权限不够. 因此,我们可以用以下方法修改用户权限: 1. su roo ...

  6. service mysqld start,Failed to start mysqld.service: Access denied

    service mysqld start 然后报: ==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===Authentic ...

  7. bazel build //tensorflow/examples/android:tensorflow_demo报错: fatal error: 'cuda_runtime.h' file not found

    In file included from ./third_party/eigen3/unsupported/Eigen/CXX11/Tensor:1:external/eigen_archive/u ...

  8. sentinel 控制台接入

    SpringBoot  Web应用== 1. 引入sentinel依赖(你可以在maven仓库查找最新版,点击直接查看) sentinel别的依赖不用引入了,这个依赖基本全部引入了. <!--接 ...

  9. 清理SqlServer日志

    最近做一个数据采集的项目,使用SQLSserver2014数据库 没想到数据才采集两三天,C盘空间已经剩下8M 连忙看了一下SQLSerevr数据库文件夹 位于 C:\Program Files\Mi ...

  10. 结构体指offsetof宏详细解析

    1.#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)     (include/linux/stddef.h) ...