性能特性测试系列1——STL容器,QT容器性能相关比较和总结

  • 流程
  • 序qtstl容器对应关系对比图
  • 顺序容器
    • 一 stdvector与QVector
    • 二 stdList与QLinkedList与 QList
    • 三 stddeque
  • 关联容器
    • 一 Stdset 与 QSetstd unordered_set
    • 二stdmap与QMap
    • 三std unordered_map与QHash
    • 四stdmultimap与QMultiMap
  • 容器适配器
    • 一std stack与QStack
    • 二stdQueue与QQueue
    • 三std priority_queue
  • 总结表格
    • 一Qt纵向
    • 二STL纵向
    • 三QT与STL
    • 四一句话
  • 参考链接

 闲话就不多扯了,本次测试了qt容器,和stl容器相关的效率,增加自己的理解,毕竟耳听为虚,眼见为实,书和资料怎么说都只是一个理论,直接测试性能才是王道。

流程

qt,stl容器对应关系对比->横向比较每个对应关系容器效率->纵向比较各自容器效率->总结表格。

测试环境:Qt5.7,vs2015。

序、qt,stl容器对应关系对比图

 下面这张图片是转载的,正好看到了。该图将qt、stl的顺序容器,和关联容器的对应关系,分别罗列出来了,一目了然,在此引用下。但是缺少容器适配器,所以我在下方自己做了个表,加入的容器适配器。
本图片转载自 点击跳转
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第1张图片
 从上图能够清楚看到一一对应的关系,所以就不多解释了,下方是自己做的一个表格,主要是补充容器适配器对应关系:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第2张图片
 本表格参考来源(cppAPI官网):点击跳转
 从代码里我们也能清楚的看到stl的容器适配器的原型:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第3张图片
 至此,基本的容器对应关系已经明了,开始比较各容器间的性能。

顺序容器

一、 std::vector与QVector:

理论
 vector的内部存储结构为线性表。在创建一个vector 后,它会自动在内存中分配一块连续的内存空间进行数据存储,当动态添加的数据超过vector 默认分配的大小时要进行内存的重新分配、拷贝与释放,所以对于随机访问的速度很快(按下标),但是对于插入尤其是在头部,中间插入元素速度很慢,在尾部插入速度很快。 并且要vector 达到最优的性能,最好在创建vector 时就指定其空间大小。
特点:随机访问快,插入慢,查找快,尾部增加快。
对比理论:
 这两者效率理论上是差不多的,且可以相互转换。QVector的insert是用下标索引,std::vector的insert是用迭代器索引。但实测中QVector一样有迭代器索引方式,并且用这两种方式效率上没有区别。
各自测试:
 1、 std::vector: 插入和删除中,从头部和中间insert插入耗时是从尾部插入的十倍,而pushback比insert要高效率很多,erase同样要比pop方式慢上千倍不止,所以除非特殊情况,一般用push和pop。
 2、 QVector:同上。
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第4张图片
 在release版本下,QVector和std::vector在尾部位置insert插入,查找上,较之std::vector稍好一点, push上没有太大区别,但是erase,popback删除上要比std::vector慢上一些;
总结:
对于vector,选择push和pop来增删都是让效率更高的方式,而在选择QVector和std::vector上没有太大的区别,但考虑到pop耗时,一般情况下std::vector更好。
 QVector≈std::vector

二、 std::List与QLinkedList与 QList:

理论:
 1、std::list和QlinkedList内部结构为双向链表(每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。)即内存区域不是连续的,元素也是在堆中存放,通过指针来进行数据的访问,因而随机访问的速度很慢(不支持下标,vector是直接找到元素地址,而它需要一个个比较),但是可以迅速地在任何节点进行插入和删除操作。因为list 的每个节点保存着它在链表中的位置,插入或删除一个元素仅对最多三个元素有所影响(而vector会对操作点之后的所有元素的存储地址都有所影响)。
 2、QList其实不是链表,是优化过的vector,官方的形容是array list。它的存储方式是分配连续的node,每个node的数据成员不大于一个指针大小,所以对于int、char等基础类型,它是直接存储(所以按照内存对齐的话,应该会内存耗损更大),对于Class、Struct等类型,它是存储对象指针,有点类似std::deque。QList的实现模式,优点主要在于快速插入。因为其元素大小不会超过sizeof(void*),所以插入时只需要移动指针,而非如vector那样移动对象。并且,由于QList存储的是void*,所以可以简单粗暴的直接用realloc()来扩容。QList的增长策略是双向增长,所以对prepend支持比vector好得多,使用灵活性高于Vector和LinkedList。缺点是每次存入对象时,需要构造Node对象,带来额外的堆开销。
特点:插入删除快,随机访问慢,查找慢,可头尾push,pop。
测试:
 Std::List: insert方式,从中间插入和从尾部插入,与push的两种方式,相差不大,但是从头部插入会稍微慢。
 QLinkedList: 同上。
 QList: insert方式,从头部插入和从尾部插入,与push两种方式相差不大,但是从中间插入相差巨大(慢很多)。,erase和两种pop方式也没差别。查找方式上支持下标访问。
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第5张图片
 总体上来说,删除和插入,QLinkedList于std::list基本上每种的操作性能都差不多,std:: list中间插入稍慢,而QList除了中间insert,比两者都要好,也和他结构相符合。

纵向比较:std::List在insert,erase上要比std::vector快上许多,pushpop上更慢,查找上也更慢。

结论:
 从各方面来说,选择QLinkedList相较于std::list其实无所谓,没什么区别。但是出去中间插入,对比QList,都要差上不少,也是官方最推荐的容器,原文是这么说的——“在大多数情况下,都推荐使用QList,它提供更加快速的插入,并且(编译后)可以展开为更少的代码量。”

三、 std::deque:

理论:
 是一种优化了的、对序列两端元素进行添加和删除操作的基本序列容器。允许较为快速地随机访问,但不像vector 把所有的对象保存在一块连续的内存块,而是由一段一段的定量连续空间构成,并且在一个映射结构中保存对这些块及其顺序的跟踪。
 理论上来说,向deque 两端添加或删除元素的开销很小。它不需要重新分配空间,所以向末端增加元素比vector 更有效(测试没更快)。实际上,deque 是对vector 和list 优缺点的结合,它是处于两者之间的一种容器。

特点:方位随机,但较vector慢。内部插入删除不及list,可以两端插入删除。
测试结果:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第6张图片
速度比较:
Insert(中间插入时,deque耗时大两者很多): std::list>std::deque>std::vector;
Push(vector没前插入): std:;vector> std::deque >std::list;
Erase: std::list>std::deque>std::vector;
Pop(vector没有前pop):std::vector≈std::deque>std::list
Find: std::vector>std::list>>std::deque
当然,和QList比,sed::deque完败。
总结:基本上,除去查找,deque就是介于std::list和std::vector间的平衡体,没有严重的短板,也能有不错的效率。

关联容器

一、 Std::set 与 QSet,std:: unordered_set

理论(基于stl):
 set本质是二叉树。所以插入只需移动指针,指向新节点就行,set使用二分法查找,所以时间复杂度为log2n,而std::set相当于有一个固定“存贮位置”的,也就是说在其它元素没有变化的情况下,把位于begin位置的元素取出来(erase),再放回去(insert),还是会处于begin的位置的。Std::set(红黑树)。QSet与std::unordered_set对应,两者皆是无序的。不允许重复值。
理论上来说:
 在插入操作和删除操作上比vector快,但查找或添加末尾的元素时会有些慢。
特点:唯一,std::set有序,插入删除快。
测试:
 QSet不提供从位置插入,只能默认方式插入。当然,指定位置插入也没有什么卵用。
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第7张图片
 1、std::set与std::unordered_set: unordered_set在查找方面上有很大优势(unordered_set是通过哈希来查找的),其余两者相差不大。
 2、QSet与std::unordered_set:默认插入方式上Qset快,find要std::unordered_set稍快,其余相差不大。
结论:
 在需要有序情况下选std::set更合适;考虑查找性能std::unordered_set更好,Qset优势不大,且只能默认插入。

二、std::map与QMap:

理论:
 二叉树。key-value对应关系的一对一的数据存储能力(pair结构)。key不可重复,且按一定顺序排列(其实我们可以将set 也看成是一种key-value关系的存储,只是它只有键没有值。它是map 的一种特殊形式)。

特点: key-value对应,有序,插入删除快。

对比结果:

性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第8张图片
 总体上来说,两者几乎没有任何性能上的差异。
纵向比较:与std::set几乎没有性能差异。
结论:总体上来说,QMap和std::map几乎没有任何性能上的差异。

三、std:: unordered_map与QHash:

理论:
 1、 C++11的新特性,原本是boost的。基本上unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。但不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。
 2、 QHash —— std::unordered_map都是各自实现了自己的hashTable,然后查询上都是用node->next的方式逐一对比,不支持互转。性能上更多的应该是看hash算法。QHash为常用的qt数据类型都提供好了qHash()函数,用户自定类型也能通过自己实现qHash()来存入QHash容器。

特点:无序,key-value,插入删除快。
测试:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第9张图片
 总体上来说,Insert方式QHash要更快,但是find上std::unordered_map远胜QHash。

纵向比较:与std::map相比,查找要比std::map效率高很多,别的几乎没有性能差异(std::的查找效率≈QHash)。

总结:考虑性能上的优势,一般情况下,用std::unordered_map更好。

四、std::multimap与QMultiMap:

理论:
 multimap的原理和map基本上是相似的,它允许“key”在容器中可以不唯一。
特点:key-value对应,有序,插入删除快。
测试:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第10张图片

横向上: std::multimap与QMultiMap在性能上几乎没有区别。

纵向上:与std::map相当,也就是说find方面比unordered_map 差。
总结:
 在std::multimap与QMultiMap这两者的选择上,没有太大的区别,性能上也大致一样。

容器适配器

一、std:: stack与QStack:

理论:
 1、栈是一种容器适配器,特别为后入先出而设计的一种(LIFO ),那种数据被插入,然后再容器末端取出。
栈实现了容器适配器,这是用了一个封装了的类作为他的特定容器,提供了一组成员函数去访问他的元素,元素从特定的容器,也就是堆栈的头取出袁术。
栈stack 的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作有求,它们都提供了push_back 、pop_back 和back 操作。也因此,标准的容器类模板vector, deque 和list可以使用,默认情况下,如果没有容器类被指定成为一个提别的stack 类,标准的容器类模板就是deque 队列。
 2、而QStack是继承自QVector的,QVector的函数基本都支持。
特点:后进先出
测试:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第11张图片
横向:结果上来看,插入和删除效率QStack要高。

纵向: QStack在插入上较QVector有优势,其他几乎没差异。

总结:只从性能上来说,QStack要高,支持函数也多。

二、std::Queue与QQueue:

理论:
 1、Std:;queue单向队列与栈有点类似,一个是在同一端存取数据,另一个是在一端存入数据,另一端取出数据。单向队列中的数据是先进先出(First In First Out,FIFO)。
队列queue 的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front 操作,因此其不能建立在vector 容器上。
 2、而QQueue继承自QList,QList的函数也基本都支持。
特点:先进先出。
测试:
性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第12张图片
横向:插入和删除std::Queue稍微好一点。
纵向: std::queue与std::stack性能几乎无差异,QQueue较QStack快,与父类QList比较几乎无差异。
总结:适用于有需求的。

三、std:: priority_queue:

理论:
 优先级队列priority_queue 适配器要求提供随机访问功能,因此不能建立在list 容器上。基于vector 容器实现。STL里面默认用的是 vector. 比较方式默认用 operator< , 所以如果后面俩个参数缺省的话,优先队列就是大顶堆,队头元素最大。
特点:优先级队列(优先级高的元素先出队列)。
测试:
这里写图片描述
纵向:速度于前两个更小。
总结:适用于有需求的

总结表格:

一、Qt纵向:

性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第13张图片

二、STL纵向:

性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第14张图片

三、QT与STL:

性能特性测试系列1——STL容器,QT容器性能相关比较和总结_第15张图片

四、一句话:

 基本上看需求选择,QT容器和STL容器差别不是很大,细节的对照表格可以看出来;

 总体上来说,map,set,mutlimap这些关联容器,性能差别不大,而无序的关联容器,也只是查找上有优势。

 最后,QList表现很抢眼。

参考链接:

–Vs编译器与qtcreator
–1Qt的容器库对比C++的STL
–2Qt的容器库对比C++的STL
–C++ STL 基本容器
–栈

你可能感兴趣的:(性能特性测试系列,c++,Qt)