前两天在群里跟人讨论到写库时对于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. Mysql TIMESTAMPDIFF测试

    select TIMESTAMPDIFF(DAY, '2015-04-20 00:00:00', '2015-04-20 23:59:59');# 只要不足24小时 为0天 select TIMEST ...

  2. List转Json函数

    public string ObjectToJson<T>(string jsonName, IList<T> IL) { StringBuilder Json = new S ...

  3. leetcode每日刷题计划-简单篇day1

    orzorz开始刷题 争取坚持每周平均下来简单题一天能做两道题吧 非常简单的题奇奇怪怪的错误orz越不做越菜 Num 7 整数反转 Reverse Integer 刚开始多给了一个变量来回折腾占地方, ...

  4. Maven私服(Nexus)资源上传下载

    1.settings.xml (向私服上传资源需要) <!-- Snapshot包的管理/Releases包的管理/第三方包管理--> <server> <id>l ...

  5. (7)linux文件常用操作命令

    ls / 查看根目录下的子节点(文件夹和文件)信息ls -al -a是显示隐藏文件 -l是以更详细的列表形式显示 **切换目录cd /home cd .. 返回上一级 **创建文件夹mkdir aaa ...

  6. Linux Shell 简介

    什么是 Shell Shell 是用户和 Linux 内核之间的接口程序,当从 Shell 或其他程序向 Linux 传递命令时,内核会做出相应的反应: Shell 是一个命令语言解释器,它拥有自己内 ...

  7. ajax-json,遇到的一个问题,jquery var ,加载顺序。JS对象,json格式转换。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. Node.js 初识2

    原文:https://www.cnblogs.com/zzuIvy/p/nodejs_1.html 测试:node.js部署网站 1.创建js2.js var http = require('http ...

  9. 查看oracle的执行计划

    基于ORACLE的应用系统很多性能问题,是由应用系统SQL性能低劣引起的,所以,SQL的性能优化很重要,分析与优化SQL的性能我们一般通过查看该SQL的执行计划,本文就如何看懂执行计划,以及如何通过分 ...

  10. HDU-1160.FatMouse'sSpeed.(LIS变形 + 路径打印)

    本题大意:给定一定数量的数对,每个数保存着一只老鼠的质量和速度,让你求出一个最长序列,这个序列按照质量严格递增,速度严格递减排列,让你输出这个序列的最长长度,并且输出组成这个最长长度的序列的对应的老鼠 ...