项目中要对一些数据结构进行存取,而项目本身对时间延时比较敏感,在使用vector还是map上着实纠结了一番,主要某些数据量比较小,才有此纠结。
而且想搞明白,到底大到什么数据量该用map?做了一些简单的测试,见下。
首先,不管是vector还是map,请尽量存取指针,否则在存取时会有数据拷贝带来不必要的时间损失。通常用int和string做key的场景比较普遍(我的项目即如此),能用int作key就用int作key,效率比string高。
测试方法:
给vector和map容器中分别塞入1,000,000个数据(顺序插入,有序的),然后设定一个查找目标,vector顺序遍历查找,map用key查找,对比查找时间。使用高精度定时器计时。
基本结论:
1、unordered_map比map快
2、vector查找14次,基本和unordered_map查找一次耗时相当(int和string做key差别不大)
3、string作key,vector查询100次,基本和map查询一次耗时相当;int作key,vector查询28次,基本和map查询一次耗时相当
4、boost的unordered_map和map与STL的效率基本相同
因此:抛弃map,用unordered_map吧。如果明确数据量小,比如小于14个,就用vector或数组,效率更高。
unordered_map<string,...>
unordered_map<int,...>
map<string, ...>
map<int,...>
测试代码:
#include <windows.h>
#include <string>
#include <iostream>
#include <iomanip>
#include <map>
#include <unordered_map>
#include <boost/unordered_map.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <algorithm>
using namespace std;
struct MyStruct
{
MyStruct() {}
MyStruct(int x, string s) : i(x), str(s) {}
int i;
string str;
};
#define VECTOR_TEST_DEF std::vector<MyStruct*>
//#define STRING_KEY
#define MAP_TYPE std::map
//#define MAP_TYPE std::unordered_map
//#define MAP_TYPE boost::unordered_map
//#define MAP_TYPE boost::interprocess::map
#define MAP_TYPE_STR "std::map"
//#define MAP_TYPE_STR "std::unordered_map"
//#define MAP_TYPE_STR "boost::unordered_map"
//#define MAP_TYPE_STR "boost::interprocess::map"
#ifdef STRING_KEY
#define MAP_TEST_DEF MAP_TYPE<string, MyStruct*>
#define TARGET_VAL "100"
#else
#define MAP_TEST_DEF MAP_TYPE<int, MyStruct*>
#define TARGET_VAL 28
#endif
#define DATA_COUNT 1000000
#define LOOP_COUNT 10000
LARGE_INTEGER m_counterFreq;
MyStruct* vectorRet;
MyStruct* mapRet;
string ToString(int d)
{
char szBuf[8];
sprintf_s(szBuf, "%d", d);
return string(szBuf);
}
LONGLONG GetMicroSecCounter()
{
LARGE_INTEGER counter = { 0 };
QueryPerformanceCounter(&counter);
return counter.QuadPart * 1000000 / m_counterFreq.QuadPart;
}
string GetTimeDiffMillSecStr(LONGLONG microCounterBegin, LONGLONG micoCounterEnd)
{
double dblDiff = (micoCounterEnd - microCounterBegin) / (double)1000;
char szBuf[32];
sprintf_s(szBuf, "%.3fms", dblDiff);
return szBuf;
}
void TestVectorMap()
{
VECTOR_TEST_DEF vecTest;
MAP_TEST_DEF mapTest;
QueryPerformanceFrequency(&m_counterFreq);
vecTest.resize(DATA_COUNT);
for (int i = 0; i < DATA_COUNT; i++)
{
string str = ToString(i);
MyStruct* t = new MyStruct(i, str);
vecTest[i] = t;
#ifdef STRING_KEY
mapTest[str] = t;
#else
mapTest[i] = t;
#endif // STRING_KEY
}
LONGLONG t1 = HighPreciseTickCounter::GetMicroSecCounter();
VECTOR_TEST_DEF::iterator iterVec;
for (int i = 0; i < LOOP_COUNT; i++)
{
iterVec = vecTest.begin();
for (; iterVec != vecTest.end(); iterVec++)
{
#ifdef STRING_KEY
if (!strcmp((*iterVec)->str.c_str(), TARGET_VAL))
#else
if ((*iterVec)->i == TARGET_VAL)
#endif // STRING_KEY
{
vectorRet = *iterVec;
break;
}
}
}
LONGLONG t2 = HighPreciseTickCounter::GetMicroSecCounter();
MAP_TEST_DEF::iterator iterMap;
for (int i = 0; i < LOOP_COUNT; i++)
{
#ifdef STRING_KEY
iterMap = mapTest.find(TARGET_VAL);
#else
iterMap = mapTest.find(TARGET_VAL);
#endif // STRING_KEY
if (iterMap != mapTest.end())
mapRet = iterMap->second;
}
LONGLONG t3 = HighPreciseTickCounter::GetMicroSecCounter();
#ifdef STRING_KEY
string keyType = "<string, MyStruct*>";
#else
string keyType = "<int, MyStruct*>";
#endif // STRING_KEY
std::cout << setiosflags(ios::left);
std::cout << setw(20) << "Find Target Val: " << TARGET_VAL << std::endl;
std::cout << setw(20) << "Find Loop Count: " << LOOP_COUNT << std::endl;
std::cout << setw(20) << "Find Cost Vector: " << GetTimeDiffMillSecStr(t1, t2) << std::endl;
std::cout << setw(20) << "Find Cost Map: " << GetTimeDiffMillSecStr(t2, t3) << " " << MAP_TYPE_STR << keyType << std::endl;
cout << setw(20) << "Vector Find: " << vectorRet->str << endl;
cout << setw(20) << "Map Find: " << mapRet->str << endl;
for (int i = 0; i < DATA_COUNT; i++)
{
MyStruct* t = vecTest[i];
delete t;
}
}