readme
判断一个STL容器是否为空,是十分常见的一种操作,常见的方法有两种,一种是判断container.size()是否为0,另一种是判断是否container.empty()。今天通过这篇博客对比一下这两种方式的效率。
两种容器区别
按照内存使用方式区可以将序列容器分为两种:连续内存容器;基于节点的容器。连续内存容器将所有的节点保存在一块连续的内存空间内,例如vector,string等等。基于节点的容器,每个节点保存在一块单独的内存内,例如list,slist等等。以下以vector和list为代表进行详细分析。
对于vector,empty()函数就是判断size()是否为0,二者做的事一样,而vector作为连续内存容器,size()函数复杂度常数。而对于list,empty()函数复杂度依然是常数,但是size()却不是,因为list容器中节点的个数必须通过遍历一遍才能知道。
测试程序
//common.h
#ifndef _COMMON_H_
#define _COMMON_H_
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <utility>
#include <algorithm>
#include <vector>
#include <iterator>
#include <sys/time.h>
using namespace std;
#endif
//Timer.h
#ifndef _TIMER_H
#define _TIMER_H
#include <sys/time.h>
#include "common.h"
class Timer
{
public:
Timer(string strCallInfo = "Timer", bool us_need = false)
{
_desc = strCallInfo;
_us_need = us_need;
gettimeofday(&_begin, NULL);
gettimeofday(&_end, NULL);
};
~Timer()
{
gettimeofday(&_end,NULL);
printf("%s, cost %ldms\n", _desc.c_str(), (_end.tv_sec - _begin.tv_sec) * 1000 + (_end.tv_usec - _begin.tv_usec) / 1000);
if (_us_need)
{
printf("%s, cost %ldus\n", _desc.c_str(), (_end.tv_sec - _begin.tv_sec) * 1000000 + _end.tv_usec - _begin.tv_usec);
}
};
private:
Timer(){};
string _desc;
bool _us_need;
timeval _begin;
timeval _end;
};
#endif
//empty_vs_size.cpp
#include "common.h"
#include "Timer.h"
const int list_size = 1000000;
list<int> test_list;
vector<int> test_vec;
int main(void)
{
for (int i = 0; i < list_size; ++i)
{
test_list.push_back(i);
test_vec.push_back(i);
}
{
Timer timer("vector size() cost");
for (int i = 0; i < 1024; ++i)
{
if (test_vec.size() == 0)
{
continue;
}
}
}
{
Timer timer("vector empty() cost");
for (int i = 0; i < 1024; ++i)
{
if (test_vec.empty())
{
continue;
}
}
}
{
Timer timer("list size() cost");
for (int i = 0; i < 1024; ++i)
{
if (test_list.size() == 0)
{
continue;
}
}
}
{
Timer timer("list empty() cost");
for (int i = 0; i < 1024; ++i)
{
if (test_list.empty())
{
continue;
}
}
}
return 0;
}
编译和执行结果
g++ -o empty_vs_size empty_vs_size.cpp -O0 -g -Wall -std=c++11
./empty_vs_size
list size() cost, cost 6036ms
list empty() cost, cost 0ms
vector size() cost, cost 0ms
vector empty() cost, cost 0ms
g++ -o empty_vs_size empty_vs_size.cpp -O2 -g -Wall -std=c++11
./empty_vs_size
list size() cost, cost 0ms
list empty() cost, cost 0ms
vector size() cost, cost 0ms
vector empty() cost, cost 0ms
测试程序解释
测试代码很简单,可以直观的从运行结果看到对于判断list是否为空而言,empty()效率要远好与size()。而对于判断vector是否为空而言,empty()和size()效率是一样的。
而对于不同的编译优化参数而言也有不同的测试结论。上述结论基于不进行任何编译后话的前提下,也即是g++ -O0。而对于适合上线的编译优化参数,例如g++ -O2,不管判断list是否为空还是判断vector是否为空,size()和empty()的效率都是一样的,至少未发现明显差距。对于编译器的优化行为,不在这边博客讨论范围内,不做进一步研究。