C++的高效从何而来

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

前一段时间,实验室的一哥们突然跑过来跟我说,“我自己写了个C的快速排序,排了一个10000000个int的数组,貌似比C库中是qsort算法要快,咋回事?C++的STL中快排(quick sort)算法的效率如何?”。

听他这么一说,我就立即做了个实验,写了如下代码:

<!-- lang: cpp -->
#include <iostream>
#include <algorithm>
#include <time.h>using namespace std;#define MAX_LEN 10000000int arri[MAX_LEN];int compare(const void* i, const void* j)
{return (*(int*)i - *(int*)j);
}int main()
{for (int i = 0; i < MAX_LEN; i++) {arri[i] = rand() % MAX_LEN;}::clock_t start, finish;//STL sortstart = ::clock();sort(arri, arri + MAX_LEN);finish = ::clock();cout << "STL sort:\t" << finish - start << "ms" << endl;for (int i = 0; i < MAX_LEN; i++) {arri[i] = rand() % MAX_LEN;}//C qsortstart = ::clock();qsort(arri, MAX_LEN, sizeof(arri[0]), compare);finish = ::clock();cout << "C qsort:\t\t" << finish - start << "ms" << endl;return 0;
}

我机器上装的是CodeBlocks(10.05)+MinGW(4.7.2)的环境。

首先,在debug模式下,CodeBlocks显示出当前的编译器命令:

<!-- lang: shell -->
mingw32-g++.exe -Wall -fexceptions  -g  -std=c++0x  -c D:\Workspaces\CodeBlocks\TestSortQ\main.cpp -o obj\Debug\main.o
mingw32-g++.exe  -o bin\Debug\TestSortQ.exe obj\Debug\main.o

运行结果:

在此输入图片描述

运行结果让我大吃一惊,STL不可能这么慢啊!后来仔细一想,这是debug模式,没有加任何优化,加上优化看看什么结果:

<!-- lang: shell -->
mingw32-g++.exe -Wall -fexceptions  -O2  -std=c++0x    -c D:\Workspaces\CodeBlocks\TestSortQ\main.cpp -o obj\Release\main.o
mingw32-g++.exe  -o bin\Release\TestSortQ.exe obj\Release\main.o   -s

运行结果:

在此输入图片描述

果然,O2选项一加上,STL sort瞬间完成了逆袭,运行时间优化了75%,而C qsort优化前后变化不是很明显,大概减少了10%。

问题来了,为什么C++标准库的快排的优化效果如此明显,而C库的快排优化不是很明显呢?

答案是inline。

我们知道,STL是泛型编程的杰出成果,里面的容器、迭代器、算法几乎都是通过泛型实现的,使得STL的通用性很强。泛型编程的一个负面效果就是破坏了接口与实现的分离,即头文件中声明,源文件中实现,源文件单独编译成库,用户只需要拿到头文件和库就可以使用了,看不到具体实现,这就是所谓的ABI,也是C的传统做法。有人会问,为什么不能做到接口与实现的分离,因为泛型编程中的函数和类,在没有接受一个模版参数之前,是没办法实例化的,只有当用户给定了模版参数的时候,编译器才会去实例化一个具体的类或函数。

如,C++ STL中的快速排序算法定义:

<!-- lang: cpp -->
template< class RandomIt >
void sort( RandomIt first, RandomIt last );

只有RandomIt这个类型真正确定了,编译器才会去实例化这个方法。在上面的代码中,我这么写:

<!-- lang: cpp -->
sort(arri, arri + MAX_LEN);

编译器通过自动类型推导,知道了RandomIt其实是一个int*,于是产生这个函数:

<!-- lang: cpp -->
sort(int*, int*);

具体实现中的RandomIt已经都替换成相应的int*,一个完整的函数就产生了。

关键的问题出现了!编译器在实例化一个函数(类也一样)的时候,它必须知道具体的实现代码,才能够产生完整的函数。这也是为什么如果大家自己写模版的时候,.h文件和.cpp文件的关系变得十分奇怪的原因,一般做饭是在.h文件末尾,#include xx.cpp,其中xx.cpp中实现了.h文件中的函数声明,或者干脆直接在.h文件中写实现。否则,如果按照一般的.h和.cpp的关系,编译器会报错,说找不到函数的实现。写过模版的程序猿应该都知道这个。

事实上,C++标准委员会为了解决这个问题,曾经引入了export关键字,来试图解决这个问题,但很少有编译器实现了(估计是实现难度较大,且增加了复杂度,得不偿失),所以这个关键字后来基本上费了。

大家或许会问,你是不是走题了,刚开始不是讨论C++的效率问题吗?怎么说了半天泛型和模版的事情了?

答案是,真是由于泛型的这个“副作用”,使得编译器可以做更多的优化!

既然编译器知道具体的实现,那么inline是编译器可以在优化上大显身手的一个手段,sort函数中需要一个compare函数(在C++中还可以通过函数对象或者操作符重载实现)来知道如何比较两个元素的大小,sort函数每次比较的时候,都会调用这个函数。对于一个10000000个元素的数组,一共会调用多少次这个compare函数是可想而知的(具体数目可以算出来),而一次函数调用的开销比较大,如栈的分配等等,这就很大程度上限制了C库中的qsort的威力,因为qsort的实现是在编译在库中的,它所调用的函数就没法inline到qsort函数里面去。但是STL是可以做到的,所以它的优化效果非常明显。

另外一个STL sort效率高的原因,在于算法的实现,不仅仅是快速排序算法,估计这也是为什么名字叫sort而不叫qsort的原因吧。在SGI STL(GNU所使用的STL)的实现中,sort函数一共采用了三种排序算法,分别是quick sort,heap sort和insert sort。使用策略如下:

1、函数主体为quick sort,但是在递归调用的时候,加上了一个参数记录迭代层数,如果迭代次数超过一定数目,转而采用heap sort。原因是如果迭代次数过多,很可能意味着quick sort落入了最坏情况(O(n2)),而heap sort的最坏情况依然是O(nlogn)。

2、当数组划分到很小的一段时,采用insert sort。原因是对于小数据量采用quick sort有些不划算(quick sort适合处理大量数据),因为quick sort本身是递归的,递归就是一次函数调用,开销较大。而insert sort在较小数据量的情况下,表现很好。

具体算法可参考SGI STL和侯捷的《STL源码剖析》。

说了这么一大堆,其实想表达的一点就是,C++为了提高效率,可以说无所不用其极,无论是STL算法实现,还是编译器的优化(语言本身为了编译器能做优化也下了很多功夫),都体现了C++的三大设计思想(或者叫做约束,可参考孟岩《关于C++复杂性的零碎思考》)之一:最高性能。

回到那位哥们的问题,为什么他的快排效率比C库的要高?我不否认他的水平,但是我感觉最大的原因还是,自己写的函数,编译器可以将compare的功能内敛到函数里面去,所以效率比C库的qsort效率要高。

关于C++的高效,我还想继续写一些文章,这篇博客且当作一个开始吧。

转载于:https://my.oschina.net/chen0dgax/blog/156156

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/265686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git本地仓库管理远程库(GitHub)——clone(下载)、commit(添加到本地仓库)、push(提交到远程仓库)、pull(拉取)操作

目录使用远程仓库的目的将本地仓库同步到git远程仓库1.克隆远程仓库(clone)2.新建一个文件3.将工作区的文件添加到暂存区4.将暂存区的文件添加到本地仓库(commit)5.提交(同步)到远程仓库(push)6.远程库拉取到本地库(pull)7.团队协作开发和跨团队协作开发(开源项目)使用远程仓库…

ps里面怎么插入流程图_学会这3个方法,5分钟能绘制出好看又高级的流程图

工作中&#xff0c;很多时候我们需要绘制流程图&#xff0c;有些小伙伴觉得流程图很难画&#xff0c;费时又耗力。那么今天小编就来给大家分享3种绘制流程图的方法&#xff0c;希望大家学会后&#xff0c;都能快速画出好看的流程图。下面就一起来看看吧~一、Excel绘制1.打开Sma…

oracle学习一二

最近在从事一个行业的测评项目&#xff0c;作为测评师来讲应当是正确的分工&#xff0c;有人负责网络安全测评&#xff0c;有人负责主机测评&#xff0c;有人负责管理测评等等。在测评一个oracle数据库的时候学习到了一点内容&#xff0c;在专业人士看来可能不足为奇&#xff0…

Github Pages 搭建个人网站

目录个人站点访问搭建步骤1.创建个人站点1&#xff09;.新建仓库2&#xff09;.填写仓库资料3&#xff09;.访问成功2.新建index.html文件1.&#xff09;点击 creat new file2.&#xff09;填写文件3&#xff09;.再次访问个人主页项目站点访问搭建步骤1.进入项目主页&#xff…

pwd命令是什么的缩写_手机学编程(2)目录管理命令

终端有两种类型&#xff1a;字符终端(CLI&#xff0c;命令行界面&#xff0c;通过键盘下达命令来要求系统帮我们做事情)和图形终端(GUI&#xff0c;图形用户界面&#xff0c;可通过鼠标下达命令来要求系统帮我们做事情)。Termux是一个字符终端&#xff0c;我们在提示符$后输入命…

Netty实现原理浅析

为什么80%的码农都做不了架构师&#xff1f;>>> 1、总体结构 先放上一张漂亮的Netty总体结构图&#xff0c;下面的内容也主要围绕该图上的一些核心功能做分析&#xff0c;但对如Container Integration及Security Support等高级可选功能&#xff0c;本文不予分析。…

如何上传文件夹到GitHub上(配图详解)

更多干货推荐可以去牛客网看看&#xff0c;他们现在的IT题库内容很丰富&#xff0c;属于国内做的很好的了&#xff0c;而且是课程刷题面经求职讨论区分享&#xff0c;一站式求职学习网站&#xff0c;最最最重要的里面的资源全部免费&#xff01;&#xff01;&#xff01;点击进…

cfg桩设备型号_什么是CFG桩?带您看下CFG桩施工工艺及流程,检测项目

一、CFG桩简介CFG(Cement Fly—ash Grave)桩是由水泥、粉煤灰、碎石、石屑或砂和水按一定配合比均匀搅拌形成的高粘结强度桩&#xff0c;和桩间土、褥垫层一起形成复合地基&#xff0c;既能较充分的发挥桩体材料的潜力&#xff0c;又可充分利用天然地基承载力&#xff0c;并能因…

django 修改日期

为什么80%的码农都做不了架构师&#xff1f;>>> http://blog.chedushi.com/archives/1389 auto_now无论是你添加还是修改对象&#xff0c;时间为你添加或者修改的时间。 auto_now_add为添加时的时间&#xff0c;更新对象时不会有变动。 转载于:https://my.oschi…

如何删除GitHub仓库里的文件夹(配图详解)

更多干货推荐可以去牛客网看看&#xff0c;他们现在的IT题库内容很丰富&#xff0c;属于国内做的很好的了&#xff0c;而且是课程刷题面经求职讨论区分享&#xff0c;一站式求职学习网站&#xff0c;最最最重要的里面的资源全部免费&#xff01;&#xff01;&#xff01;点击进…

jquery实现截取pc图片_如何优雅的对网页截取长图

苏生不惑第115 篇原创文章&#xff0c;将本公众号设为星标&#xff0c;第一时间看最新文章。最近写文章想截个长图&#xff0c;才发现一直使用的QQ早有这个功能了&#xff0c;这里就整理几个pc上网页长截图的方案。qq滚动截图qq截图应该很多人用过&#xff0c;我平常挂qq也只是…

在Bootstrap开发框架中使用bootstrap-datepicker插件

在基于Boostrap的Web开发中&#xff0c;往往需要录入日期内容&#xff0c;基于Boostrap的插件中&#xff0c;关于日期的录入可以使用bootstrap-datepicker这个非常不错的插件&#xff0c;以替代默认的typedate这种不太友好的日期录入控件&#xff0c;本篇介绍的是我在我的Boost…

STM32 ADC转换实验

摘自&#xff1a;STM32 ADC转换实验 作者&#xff1a;追兮兮 发布时间&#xff1a; 2020-10-29 09:42:24 网址&#xff1a;https://blog.csdn.net/weixin_44234294/article/details/109333307 STM32 ADC 简介 STM32 拥有 1~3 个 ADC&#xff08;STM32F101/102 系列只有 1 个 AD…

ubuntu下搭载LNMP环境,解决 fpm监听失败

为什么80%的码农都做不了架构师&#xff1f;>>> 1.安装mysql sudo apt-get install mysql-server mysql-client 安装过程中要输入root用户的密码。 2.安装nginx sudo apt-get install nginx 2.安装php 1. sudo apt-get install php5-fpm php5-cgi php5-mysql p…

极限与连续知识点总结_高数上知识点期末复习 极限、连续、间断点(一)

点击蓝字关注我们No.1函数分值题型解析1题型解析2tips&#xff1a;为了帮助同学们更好的通过高数期末考试&#xff0c;不挂科&#xff0c;我们最近正在加紧制作《高等数学》上册的期末复习冲刺课程&#xff0c;包含讲解视频和课程讲义。课程即将上线&#xff0c;敬请期待......…

数据结构:八大数据结构分类

摘自&#xff1a;数据结构&#xff1a;八大数据结构分类 作者&#xff1a;鄙人薛某 发布时间&#xff1a;2018-09-05 18:23:28 网址&#xff1a;https://blog.csdn.net/yeyazhishang/article/details/82353846 本文目录&#xff1a; 数据结构分类1、数组2、栈3、队列4、链表5、…

三线调速风扇原理_学修电风扇~风机转速慢、调速失灵故障维修。

一、电风扇转速慢转速慢的故障原因有电源电压过低、起动电容器损坏及电动机本身性能不良或轴承润滑不良。对于微电脑控制式电风扇出现转速慢的故障现象&#xff0c;有可能是双向晶闸管驱动电路性能不良造成的。落地式电风扇、台式电风扇及鸿运扇出现此类故障时的检查方法大致相…

数据结构——链式队列解析(C语言版)

摘自&#xff1a;数据结构学习——链式队列解析&#xff08;C语言版&#xff09; 作者&#xff1a;正弦定理 发布时间&#xff1a;2020-11-26 21:07:08 网址&#xff1a;https://blog.csdn.net/chinesekobe/article/details/110203428 数据结构——链队列解析过程和简单代码实现…

中班机器人教室设计方案_奇思妙想一起玩,机器人来了安格利亚东郡生态幼儿园亲子活动...

奇思妙想一起玩机器人总动员亲子活动课程起源有一天午休起床几个小女生正排队梳辫子&#xff0c;因为需要等待&#xff0c;孩子们比较无聊就在看教室的摆件&#xff0c;突然小朋友们就用机器人的语调开始对话&#xff0c;我听到后感觉很有意思也用机器人的语调和他们对话&#…

数据结构——顺序栈和链式栈的简单实现和解析(C语言版)

摘自&#xff1a;数据结构学习——顺序栈和链式栈的简单实现和解析&#xff08;C语言版&#xff09; 作者&#xff1a;正弦定理 发布时间&#xff1a;2020-11-26 21:26:49 网址&#xff1a;https://blog.csdn.net/chinesekobe/article/details/110205257 数据结构——栈的简单解…