测试条件:
gcc version 4.2.1 20070719 [FreeBSD]
FreeBSD 7.2-RELEASE #0: Fri May 1 07:18:07 UTC 2009 [email protected]:/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 0K 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; }