[转载] 6 个技巧,提升 C++11 的 vector 性能
转载:https://www.sohu.com/a/120595688_465979
Vector 就像是 C++ STL 容器的瑞士军刀。Bjarne Stoutsoup 有一句话 – “一般情况下,如果你需要容器,就用 vector”。像我们这样的普通人把这句话当作真理,只需要照样去做。然而,就像其它工具一样,vector 也只是个工具,它能提高效率,也能降低效率。
这篇文章中我们可以看到 6 种优化使用 vector 的方法。我们会在最常见的使用 vector 的开发任务中看到有效的方法和无效的方法,并以此衡量有效使用 vector 会带来怎样的性能提升,并试图理解为什么能得到这样的性能提升。
性能测试的搭建和方法:
所有测试都在我的 Surface Book 中运行,这台笔记本拥有主频 2.6Ghz 的酷睿 i7 处理器,8 GB 内存,安装了 Windows 10 操作系统并使用 VS2015 C++ 编译器编译运行。
我们会使用 Stopwatch。这个工具由 Kjell 创建,在 可以找到。
我们会运行每个测试 100 次,然后计算平均运行时间来作为依据。运行测试的代码在。你可以自由下载,用于在你自己的系统中评估 vector 的性能。那里提供的代码段只反映了一次循环,这让事件变得简单。
我们在 vector 中存入 TestStruct 结构的数据,并使用 FillVector() 来填充 vector。它们的定义如下。
马上开始在 C++ 11 中优化 vector 用法的介绍。
#1 提前分配足够的空间以避免不必要的重新分配和复制周期
程序员喜欢使用 vector,因为他们只需要往向容器中添加元素,而不用事先操心容器大小的问题。但是,如果由一个容量为 0 的 vector 开始,往里面添加元素会花费大量的运行性能。如果你之前就知道 vector 需要保存多少元素,就应该提前为其分配足够的空间。
这里有一个简单的示例,往 vector 里添加 1 万个测试结构的实例——先进行不预分配空间的测试再进行有预分配的测试。
在我的计算机中,未预分配空间的情况用了 5145 微秒(us),而预分配了空间的情况下只用了 1279 微秒,性能提高了 75.14%!!!
这个情况在 Scott Meyers 的书中得到了很好的解释,这本书叫 :
“对于 vector 和 string,在需要更多空间的时候,会做与 realloc 等效的事情。这种类似 realloc 的操作有4个步骤:
1. 分别一个新的内存块,其容量是容器当前容量的数倍。多数实现中,vector 和 string 容量的提升因子在 1.5 和 2 之间。
2. 从容器原来占用的内存中将元素拷贝到新分配的内存中。
3. 释放原有内存中的对象。
4. 释放原有内存。
有了所有这些操作:分配、回收、拷贝和释放,如果说这些步骤(对于性能)极其昂贵,你一点都不应该感到惊讶。当然,你肯定不希望频繁的进行这样的操作。如果这还没有打动你,那么想想每次进行这些步骤的时候,vector 和 string 中所有的迭代器、指针和引用都会失效。这意味着一个简单的插入操作,对于其它使用了当前 vector 或 string 中的迭代器、指针或引用的数据结构,都有可能引起对它们进行更新。”
2.使用 shrink_to_fit() 释放 vector 占用的内存, – clear() 或 erase() 不会释放内存。
与大家所想的相反,使用 erase() 或 clear() 从 vector 中删除元素并不会释放分配给 vector 的内存。做个简单的实验就可以证明这一点。我们往一个 vector 中添加 100 个元素,然后在这个 vector 上调用 clear() 和 erase()。然后我们可以让 capacity() 函数告诉我们为这个容器分配的内存可以存入多少元素。
下面是输出:
从上面的输出可以看到,erase() 或 clear() 不会减少 vector 占用的内存。如果在代码中到达某一点,不再需要 vector 时候,请使用 std::vector::shrink_to_fit() 方法释放掉它占用的内存。
请注意,shrink_to_fit() 可能没有被所有编译器供应商完全支持。这种情况下,可以使用“Swap 惯用法”来清空 vector,代码如下:
container<T>( c ).swap( c ); // shrink-to-fit 惯用法,用于清空存储空间
container<T>().swap( c ); // 用于清空所有内容和存储空间的惯用法
如果你对此感兴趣,请查看“”一书的条款# 82,其中有针对 swap 惯用法的细节介绍。
3. 在填充或者拷贝到 vector 的时候,应该使用赋值而不是 insert() 或push_back().
从一个 vector 取出元素来填充另一个 vector 的时候,常有三种方法 – 把旧的 vector 赋值给新的 vector,使用基于迭代器的 std::vector::insert() 或者使用基于循环的 std::vector::push_back()。这些方法都展示在下面:
这是它们的性能:
赋值: 589.54 us
insert(): 1321.27 us
push_back(): 5354.70 us
我们看到 vector 赋值比 insert() 快了 55.38%,比 push_back() 快了 89% 。
为什么会这样???
赋值非常有效率,因为它知道要拷贝的 vector 有多大,然后只需要通过内存管理一次性拷贝 vector 内部的缓存。
所以,想高效填充 vector,首先应尝试使用 assignment,然后再考虑基于迭代器的 insert(),最后考虑 push_back。当然,如果你需要从其它类型的容器拷贝元素到 vector 中,赋值的方式不可行。这种情况下,只好考虑基于迭代器的 insert()。
4. 遍历 std::vector 元素的时候,避免使用 std::vector::at() 函数。
遍历 vector 有如下三种方法:
使用迭代器
使用 std::vector::at() 成员函数
使用下标 – [ ] 运算符
下面展示了每种用法:
输出是:
显而易见,用 std::vector::at() 函数访问 vector 元素是最慢的一个。
5. 尽量避免在 vector 前部插入元素
任何在 vetor 前部部做的插入操作其复杂度都是 O(n) 的。在前部插入数据十分低效,因为 vector 中的每个元素项都必须为新的项腾出空间而被复制。如果在 vector 前部连续插入多次,那可能需要重新评估你的总体架构。
做个有趣的尝试,下面是在 std::vector 前部做插入和在 std::list 前部部做插入的对比:
如果我运行这个测试10,其中使用一个包含100个元素的vector,那么输出结果如下:
在 list 前部部插入操作比在 vector 前部部快大约58836%。不用感到奇怪,因为在 list 前部做元素插入的算法,其复杂度为 O(1)。显然,vector 包含元素越多,这个性能测试的结果会越差。
6. 在向 vector 插入元素的时候使用 emplace_back() 而不是 push_back()。
几乎赶上 C++11 潮流的每个人都明确地认同“安置”这种往 STL 容器里插入元素的方法。理论上来说,“安置”更有效率。然而所有实践都表明,有时候性能差异甚至可以忽略不计。
思考下面的代码:
如果运行100次,会得到这样的输出:
可以清楚的看到,“安置”函数比插入函数性能更好 – 但只有 177 微秒的差距。在所有情况下,他们大致是相当的。
仅在以下情况下,Emplacement 函数可能会更快:
要添加的值是在 vector 中构造的,而不是赋值的。
传递的参数类型与 vector 中保存的类型不同。例如,如果一个向量包含 std :: string,但我们传递一个字符串值到该 vector。
即使上述两个条件都不成立,如本例所示的,你也不要因为在插入时使用 emplacement 而掉以轻心。
更多关于 emplacement vs. insertion 的详细信息,请查看 Scott Meyer 的““一书中的条目#42。
结语
与任何第三方统计数据一样,你不应盲目地依赖此处提供的结果和建议。在不同的操作系统、处理器体系结构和编译器设置上测试时,你可能遇到很多不确定因素。因此,你需要根据实际数据,自己做出衡量。
[转载] 6 个技巧,提升 C++11 的 vector 性能的更多相关文章
- [转载]Js小技巧||给input type=“password”的输入框赋默认值
http://www.cnblogs.com/Raywang80s/archive/2012/12/06/2804459.html [转载]Js小技巧||给input type="passw ...
- 【转】提升你的Java应用性能:改善数据处理
提升你的Java应用性能:改善数据处理 作者:贾小骏 发布于07月26日 10:17 许多应用程序在压力测试阶段或在生产环境中都会遇到性能问题.如果我们看一下性能问题背后的原因,会发现很多是由数据处 ...
- C++11在时空性能方面的改进
C++11在时空性能方面的改进 这篇我们聊聊C++11在时间和空间上的改进点: 主要包括以下方面: 新增的高效容器:array.forward_list以及unordered containers: ...
- C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路
C#不用union,而是有更好的方式实现 用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...
- 提升 web 应用程序的性能(一)
提升 web 应用程序的性能,找出瓶颈,加快客户端内容的速度. 作为 web 用户,我们知道页面加载或刷新的速度对其成功至关重要.本文将帮助您更好地理解影响 web 应用程序性能的因素.学 ...
- 开启 NFS 文件系统提升 Vagrant 共享目录的性能
Vagrant 默认的 VirtualBox 共享目录方式读写性能表现并不好,好在 Vagrant 支持 NFS 文件系统方式的共享,我们可以启用 NFS 提升性能 开启方法 首先要把虚拟机的网络设置 ...
- 【C/C++开发】容器set和multiset,C++11对vector成员函数的扩展(cbegin()、cend()、crbegin()、crend()、emplace()、data())
一.set和multiset基础 set和multiset会根据特定的排序准则,自动将元素进行排序.不同的是后者允许元素重复而前者不允许. 需要包含头文件: #include <set> ...
- 11个显著提升 ASP.NET 应用程序性能的技巧——第1部分
[编者按]本文出自站外作者 Brij Bhushan Mishra ,Brij 是微软 MVP-ASP.NET/IIS.C# Corner MVP.CodeProject Insider,前 Code ...
- (转载)DataTable使用技巧总结
在项目中经常用到DataTable,如果DataTable使用得当,不仅能使程序简洁实用,而且能够提高性能,达到事半功倍的效果,现对DataTable的使用技巧进行一下总结. 一.Da ...
- [转载]8 种提升 ASP.NET Web API 性能的方法
http://www.oschina.net/translate/8-ways-improve-asp-net-web-api-performance 英文原文:8 ways to improve A ...
随机推荐
- mongodb 中rs.stauts()命令参数解析
转载请注明出处: rs.status()命令用于获取MongoDB副本集的状态信息.它提供了关于副本集中各个节点的详细信息,包括节点的健康状况.角色.选举状态等. 以下是查看一个mongo集群状态返回 ...
- flink 大批量任务提交 yarn 失败问题
问题现象 用户迁移到新集群后,反馈他们开发平台大量 flink 任务提交失败了,当时集群的 yarn 资源是足够的 排查过程 用户是在他们的开发平台上提交的,查看他们失败的任务,发现是他们提交端主动 ...
- Project: Kill e
接到上级任务,今天来暗杀 \(e\) 据说杀死 \(e\) 的方式就是把他算出来,好吧,现在我们还是来算一下 考虑使用如下代数式求解 \[e\ \text{site:baidu.com} \] 虽然我 ...
- Java poi 读取 word 、 pdf
从各个博客 CV 出来的,不好意思 pom <dependency> <groupId>org.apache.poi</groupId> <artifactI ...
- CTF中特别小的EXE是怎么生成的
我们在打CTF时候,出题的爷爷们给出的exe都很小 就10k左右,有的甚至就5k,那时候我很郁闷啊.现在我也能了啊哈哈 不多bb按如下操作: 我们来看看正常的release生成的代码 #include ...
- Linux-USB驱动笔记-Gadget Function驱动
1.前言 在Linux-USB驱动笔记(四)–USB整体框架中有説到Gadget Function驱动,下面我们来具体看一下. Gadget Function就是指设备的功能,比如作为U盘,需要文件存 ...
- 新思路,基于Diffusion的初始化权重生成策略 | ECCV'24
良好的权重初始化可以有效降低深度神经网络(DNN)模型的训练成本.如何初始化参数的选择是一个具有挑战性的任务,可能需要手动调整,这可能既耗时又容易出错.为了解决这些限制,论文迈出了建立权重生成器以合成 ...
- 数据库系统原理——第三章 关系数据库标准语言SQL
@ 目录 1.SQL的特点 2.SQL的组成 3SQL语句 3.1数据库的基本操作 3.2 基本表的定义.修改.删除 3.3索引的建立与删除 3.4数据更新 3.5数据查询 3.5.1单表查询 3.5 ...
- CSS动画(波光粼粼登录页面)
1.整体效果 https://mmbiz.qpic.cn/sz_mmbiz_gif/EGZdlrTDJa4AbemkU3vLRIDzTIgPHSjicia97wfvMVAhqZL4lsGbQQCbsV ...
- 周六晚11实习生上线数据观测突发问题(涉及MYSQL,Hive等)
前提 有点恐怖,上次需求上线后,部分线上数据观测要留到11月初,上次是一个税收相关的需求,已有功能的线上数据观察已经完成,还剩下部分只有在十一月初才可以观察 简单提一嘴(非技术相关) 之前把hive ...