map hash_map unordered_map 性能测试
2015-08-06 15:54
337 查看
转自:http://blog.chinaunix.net/uid-20384806-id-3055333.html
测试条件:
gcc version 4.2.1 20070719 [FreeBSD]
FreeBSD 7.2-RELEASE #0: Fri May 1 07:18:07 UTC 2009 root@driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 16核
测试程序说明:
先准备好n个字符串随机的MD5字符串做为key,然后分别对3个容器进行插入、遍历、查找、删除操作。
例如,n=100的时候,插入是指插入100个随机MD5 key;遍历是指对容器遍历一次;查找是指分别对这个100个随机的MD5 key做查找操作(即查找100次);删除是指挨个删除这个100个随机MD5 key。
测试数据如下表:
结论:
1. std::tr1::unordered_map 与 std::ext/hash_map
任何情况下,如果要在这两个容器之间选择的话,我们毫不犹豫应该选择 unordered_map。因为他的性能在上述4中操作中均优于 hash_map,甚至可以说远远优于 hash_map。
2. std::tr1::unordered_map 与 std::map
map的性能总体来说是最差的。但是,当我们需要一个有序的关联容器的时候,我们必须选择std::map,因为 unordered_map 内部元素不是有序的,这一点从名字都可以看出来。除此之外都应该选择 unordered_map 。
3. 上述测试中,unordered_map 的遍历性能几乎是常数级别的,与常识不太相符,需要再研究研究。
附录:贴上源代码
说明:与测试程序稍有区别,这里的源码里没有MD5相关的代码以确保其他人能比较方便的直接拿去编译运行。
如有错误还请跟帖指出,非常感谢。
#include <iostream>
#include <string>
#include <sstream>
#include <list>
#include <map>
#include <sys/time.h>
#include <ext/hash_map>
#include <tr1/unordered_map>
namespace zl
{ //{{{
struct equal_to
{
bool operator()(const char* s1,
const char* s2)
const
{
return strcmp(s1, s2)
== 0;
}
};
struct hash_string
:
public std::unary_function<std::string,
std::size_t>
{
std::size_t
operator()(const std::string&
__s) const
#ifdef __linux__
{ return std::tr1::Fnv_hash<>::hash(__s.data(),
__s.length());
}
#else
{ return std::tr1::_Fnv_hash<>::hash(__s.data(),
__s.length());
}
#endif
};
struct hash_charptr
:
public std::unary_function<const char*,
std::size_t>
{
std::size_t
operator()(const char* __s)
const
#ifdef __linux__
{ return std::tr1::Fnv_hash<>::hash(__s,
strlen(__s));
}
#else
{ return std::tr1::_Fnv_hash<>::hash(__s,
strlen(__s));
}
#endif
};
}//}}}
typedef std::list<std::string>
string_list;
typedef std::map<std::string,
int> string_map;
typedef __gnu_cxx::hash_map<std::string,
int, zl::hash_string> string_hash_map;
typedef std::tr1::unordered_map<std::string,
int> string_unordered_map;
void fill_list(string_list& slist, size_t count);
uint64_t current_usec();
int main(
int argc, char* argv[]
)
{
if
(argc != 2
&& argc
!= 3)
{
fprintf(stderr,
"Usage:%s test_count rehash\n", argv[0]);
fprintf(stderr,
"For example:%s 10000 rehash\n", argv[0]);
return -1;
}
size_t count = atoi(argv[1]);
bool rehash =
false;
if
(argc == 3)
{
rehash =
true;
}
string_map smap;
string_hash_map shash_map;
string_unordered_map sunordered_map;
if
(rehash)
{
sunordered_map.rehash(count);
}
string_list slist;
fill_list(slist, count);
uint64_t start = 0;
uint64_t end
= 0;
uint64_t map_insert_us = 0;
uint64_t hash_map_insert_us = 0;
uint64_t unordered_map_insert_us = 0;
uint64_t map_traverse_us = 0;
uint64_t hash_map_traverse_us = 0;
uint64_t unordered_map_traverse_us = 0;
uint64_t map_find_us = 0;
uint64_t hash_map_find_us = 0;
uint64_t unordered_map_find_us = 0;
uint64_t map_delete_us = 0;
uint64_t hash_map_delete_us = 0;
uint64_t unordered_map_delete_us = 0;
// Insert test
{//{{{
string_list::iterator it(slist.begin());
string_list::iterator ite(slist.end());
//map insert
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
smap[*it]
= i;
}
end
= current_usec();
map_insert_us =
end - start;
//hash_map insert
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
hash_map_insert_us =
end - start;
//unordered_map insert
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
unordered_map_insert_us =
end - start;
}//}}}
// Traverse test
{//{{{
//map traverse
{
string_map::iterator it(smap.begin());
string_map::iterator ite(smap.end());
start = current_usec();
for
(int i
= 0; it
!= ite;
++it)
{
i++;
}
end
= current_usec();
map_traverse_us =
end - start;
}
//hash_map traverse
{
string_hash_map::iterator it(shash_map.begin());
string_hash_map::iterator ite(shash_map.end());
start = current_usec();
for
(int i
= 0; it
!= ite;
++it)
{
i++;
}
end
= current_usec();
hash_map_traverse_us =
end - start;
}
//unordered_map traverse
{
string_unordered_map::iterator it(sunordered_map.begin());
string_unordered_map::iterator ite(sunordered_map.end());
start = current_usec();
for
(int i
= 0; it
!= ite;
++it)
{
i++;
}
end
= current_usec();
unordered_map_traverse_us =
end - start;
}
}//}}}
// Find test
{//{{{
string_list::iterator it(slist.begin());
string_list::iterator ite(slist.end());
//map find
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
smap[*it]
= i;
}
end
= current_usec();
map_find_us =
end - start;
//hash_map find
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
hash_map_find_us =
end - start;
//unordered_map find
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
unordered_map_find_us =
end - start;
}//}}}
// Delete test
{//{{{
string_list::iterator it(slist.begin());
string_list::iterator ite(slist.end());
//map delete
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
smap.erase(*it);
}
end
= current_usec();
map_delete_us =
end - start;
//hash_map delete
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map.erase(*it);
}
end
= current_usec();
hash_map_delete_us =
end - start;
//unordered_map delete
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map.erase(*it);
}
end
= current_usec();
unordered_map_delete_us =
end - start;
}//}}}
//stat output
std::cout
<<
" insert, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_insert_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_insert_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_insert_us
<<
" us\n";
std::cout
<<
"\n";
std::cout
<<
" traverse, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_traverse_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_traverse_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_traverse_us
<<
" us\n";
std::cout
<<
"\n";
std::cout
<<
" find, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_find_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_find_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_find_us
<<
" us\n";
std::cout
<<
"\n";
std::cout
<<
" delete, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_delete_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_delete_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_delete_us
<<
" us\n";
return 0;
}
void fill_list(string_list& slist, size_t count)
{
for
(size_t i = 0; i
< count;
++i)
{
std::ostringstream oss;
oss << i;
//slist.push_back(MD5::getHexMD5(oss.str().c_str(),
oss.str().length()));
slist.push_back(oss.str());
}
}
uint64_t current_usec()
{
struct timeval tv;
gettimeofday(
&tv,
NULL );
return tv.tv_sec
* 1000 * 1000
+ tv.tv_usec;
}
测试条件:
gcc version 4.2.1 20070719 [FreeBSD]
FreeBSD 7.2-RELEASE #0: Fri May 1 07:18:07 UTC 2009 root@driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 16核
测试程序说明:
先准备好n个字符串随机的MD5字符串做为key,然后分别对3个容器进行插入、遍历、查找、删除操作。
例如,n=100的时候,插入是指插入100个随机MD5 key;遍历是指对容器遍历一次;查找是指分别对这个100个随机的MD5 key做查找操作(即查找100次);删除是指挨个删除这个100个随机MD5 key。
测试数据如下表:
插入,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 241 | 2833 | 35888 | 381214 | 4439088 | 62233380 |
std::ext/hash_map | 97 | 1667 | 16466 | 146025 | 1788446 | 18512639 |
std::tr1::unordered_map | 77 | 772 | 8052 | 53094 | 658312 | 7429297 |
遍历,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 11 | 76 | 842 | 11603 | 155700 | 1771906 |
std::ext/hash_map | 47 | 430 | 4218 | 39880 | 470344 | 4781575 |
std::tr1::unordered_map | 1 | 1 | 2 | 1 | 2 | 1 |
查找,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 156 | 2111 | 30456 | 258709 | 4100260 | 59064394 |
std::ext/hash_map | 77 | 774 | 8056 | 56974 | 660231 | 7705527 |
std::tr1::unordered_map | 77 | 772 | 8051 | 54456 | 659537 | 7600263 |
删除,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 291 | 3641 | 49584 | 472414 | 6675897 | 92491113 |
std::ext/hash_map | 89 | 869 | 9068 | 86524 | 964767 | 10372650 |
std::tr1::unordered_map | 49 | 480 | 4879 | 33087 | 395098 | 4369617 |
1. std::tr1::unordered_map 与 std::ext/hash_map
任何情况下,如果要在这两个容器之间选择的话,我们毫不犹豫应该选择 unordered_map。因为他的性能在上述4中操作中均优于 hash_map,甚至可以说远远优于 hash_map。
2. std::tr1::unordered_map 与 std::map
map的性能总体来说是最差的。但是,当我们需要一个有序的关联容器的时候,我们必须选择std::map,因为 unordered_map 内部元素不是有序的,这一点从名字都可以看出来。除此之外都应该选择 unordered_map 。
3. 上述测试中,unordered_map 的遍历性能几乎是常数级别的,与常识不太相符,需要再研究研究。
附录:贴上源代码
说明:与测试程序稍有区别,这里的源码里没有MD5相关的代码以确保其他人能比较方便的直接拿去编译运行。
如有错误还请跟帖指出,非常感谢。
#include <iostream>
#include <string>
#include <sstream>
#include <list>
#include <map>
#include <sys/time.h>
#include <ext/hash_map>
#include <tr1/unordered_map>
namespace zl
{ //{{{
struct equal_to
{
bool operator()(const char* s1,
const char* s2)
const
{
return strcmp(s1, s2)
== 0;
}
};
struct hash_string
:
public std::unary_function<std::string,
std::size_t>
{
std::size_t
operator()(const std::string&
__s) const
#ifdef __linux__
{ return std::tr1::Fnv_hash<>::hash(__s.data(),
__s.length());
}
#else
{ return std::tr1::_Fnv_hash<>::hash(__s.data(),
__s.length());
}
#endif
};
struct hash_charptr
:
public std::unary_function<const char*,
std::size_t>
{
std::size_t
operator()(const char* __s)
const
#ifdef __linux__
{ return std::tr1::Fnv_hash<>::hash(__s,
strlen(__s));
}
#else
{ return std::tr1::_Fnv_hash<>::hash(__s,
strlen(__s));
}
#endif
};
}//}}}
typedef std::list<std::string>
string_list;
typedef std::map<std::string,
int> string_map;
typedef __gnu_cxx::hash_map<std::string,
int, zl::hash_string> string_hash_map;
typedef std::tr1::unordered_map<std::string,
int> string_unordered_map;
void fill_list(string_list& slist, size_t count);
uint64_t current_usec();
int main(
int argc, char* argv[]
)
{
if
(argc != 2
&& argc
!= 3)
{
fprintf(stderr,
"Usage:%s test_count rehash\n", argv[0]);
fprintf(stderr,
"For example:%s 10000 rehash\n", argv[0]);
return -1;
}
size_t count = atoi(argv[1]);
bool rehash =
false;
if
(argc == 3)
{
rehash =
true;
}
string_map smap;
string_hash_map shash_map;
string_unordered_map sunordered_map;
if
(rehash)
{
sunordered_map.rehash(count);
}
string_list slist;
fill_list(slist, count);
uint64_t start = 0;
uint64_t end
= 0;
uint64_t map_insert_us = 0;
uint64_t hash_map_insert_us = 0;
uint64_t unordered_map_insert_us = 0;
uint64_t map_traverse_us = 0;
uint64_t hash_map_traverse_us = 0;
uint64_t unordered_map_traverse_us = 0;
uint64_t map_find_us = 0;
uint64_t hash_map_find_us = 0;
uint64_t unordered_map_find_us = 0;
uint64_t map_delete_us = 0;
uint64_t hash_map_delete_us = 0;
uint64_t unordered_map_delete_us = 0;
// Insert test
{//{{{
string_list::iterator it(slist.begin());
string_list::iterator ite(slist.end());
//map insert
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
smap[*it]
= i;
}
end
= current_usec();
map_insert_us =
end - start;
//hash_map insert
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
hash_map_insert_us =
end - start;
//unordered_map insert
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
unordered_map_insert_us =
end - start;
}//}}}
// Traverse test
{//{{{
//map traverse
{
string_map::iterator it(smap.begin());
string_map::iterator ite(smap.end());
start = current_usec();
for
(int i
= 0; it
!= ite;
++it)
{
i++;
}
end
= current_usec();
map_traverse_us =
end - start;
}
//hash_map traverse
{
string_hash_map::iterator it(shash_map.begin());
string_hash_map::iterator ite(shash_map.end());
start = current_usec();
for
(int i
= 0; it
!= ite;
++it)
{
i++;
}
end
= current_usec();
hash_map_traverse_us =
end - start;
}
//unordered_map traverse
{
string_unordered_map::iterator it(sunordered_map.begin());
string_unordered_map::iterator ite(sunordered_map.end());
start = current_usec();
for
(int i
= 0; it
!= ite;
++it)
{
i++;
}
end
= current_usec();
unordered_map_traverse_us =
end - start;
}
}//}}}
// Find test
{//{{{
string_list::iterator it(slist.begin());
string_list::iterator ite(slist.end());
//map find
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
smap[*it]
= i;
}
end
= current_usec();
map_find_us =
end - start;
//hash_map find
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
hash_map_find_us =
end - start;
//unordered_map find
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map[*it]
= i;
}
end
= current_usec();
unordered_map_find_us =
end - start;
}//}}}
// Delete test
{//{{{
string_list::iterator it(slist.begin());
string_list::iterator ite(slist.end());
//map delete
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
smap.erase(*it);
}
end
= current_usec();
map_delete_us =
end - start;
//hash_map delete
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map.erase(*it);
}
end
= current_usec();
hash_map_delete_us =
end - start;
//unordered_map delete
it = slist.begin();
start = current_usec();
for
(int i
= 0; it
!= ite;
++it,
++i)
{
shash_map.erase(*it);
}
end
= current_usec();
unordered_map_delete_us =
end - start;
}//}}}
//stat output
std::cout
<<
" insert, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_insert_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_insert_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_insert_us
<<
" us\n";
std::cout
<<
"\n";
std::cout
<<
" traverse, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_traverse_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_traverse_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_traverse_us
<<
" us\n";
std::cout
<<
"\n";
std::cout
<<
" find, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_find_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_find_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_find_us
<<
" us\n";
std::cout
<<
"\n";
std::cout
<<
" delete, count "
<< count
<< std::endl;
std::cout
<<
" std::map "
<< map_delete_us
<<
" us\n";
std::cout
<<
" std::ext/hash_map "
<< hash_map_delete_us
<<
" us\n";
std::cout
<<
"std::tr1::unordered_map "
<< unordered_map_delete_us
<<
" us\n";
return 0;
}
void fill_list(string_list& slist, size_t count)
{
for
(size_t i = 0; i
< count;
++i)
{
std::ostringstream oss;
oss << i;
//slist.push_back(MD5::getHexMD5(oss.str().c_str(),
oss.str().length()));
slist.push_back(oss.str());
}
}
uint64_t current_usec()
{
struct timeval tv;
gettimeofday(
&tv,
NULL );
return tv.tv_sec
* 1000 * 1000
+ tv.tv_usec;
}
相关文章推荐
- submit中onclick方法的执行
- HIVE窗口函数
- java中重载与重写的区别
- Java疑问:-------在Socket中用PrintWriter 写入的数据为什么用DataInputStream不能读取?
- Linux时间子系统之四:定时器的引擎:clock_event_device
- html+js实现图片上传前预先预览
- 内核对象句柄的继承
- java代码优化
- 怎么写网页title标题..
- html+js实现图片上传前预先预览
- canvas操作图像
- #笔记#圣思园 JavaWeb 第59讲——使用Firebug调试JS、JSUnit,XUnit
- javascript实现3D变换的立体圆圈实例
- js操作数组方法
- Android 中 Handler 引起的内存泄露
- Web Service_cxf(转载)
- Linux时间子系统之三:时间的维护者:timekeeper
- ZZUACM 2015 暑假集训 round 01
- MyBatis学习总结(六)——调用存储过程
- c# 自定义属性 属性类型为枚举