map unordered_map hash_map比一比

分類: C/C++

by zieckey

測試條件:
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 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相關的代碼以確保其他人能比較方便的直接拿去編譯運行。

如有錯誤還請跟帖指出,非常感謝。


  1. #include <iostream>
  2. #include <string>
  3. #include <sstream>
  4. #include <list>
  5. #include <map>
  6. #include <sys/time.h>
  7. #include <ext/hash_map>
  8. #include <tr1/unordered_map>

  9. namespace zl
  10. { //{{{
  11.     struct equal_to
  12.     {
  13.         bool operator()(const char* s1, const char* s2) const
  14.         {
  15.             return strcmp(s1, s2) == 0;
  16.         }
  17.     };
  18.  
  19.     struct hash_string
  20.         : public std::unary_function<std::string, std::size_t>
  21.         {
  22.             std::size_t
  23.                 operator()(const std::string& __s) const
  24. #ifdef __linux__
  25.                 { return std::tr1::Fnv_hash<>::hash(__s.data(), __s.length()); }
  26. #else
  27.                 { return std::tr1::_Fnv_hash<>::hash(__s.data(), __s.length()); }
  28. #endif
  29.         };

  30.     
  31.     struct hash_charptr
  32.         : public std::unary_function<const char*, std::size_t>
  33.         {
  34.             std::size_t
  35.                 operator()(const char* __s) const
  36. #ifdef __linux__
  37.                 { return std::tr1::Fnv_hash<>::hash(__s, strlen(__s)); }
  38. #else
  39.                 { return std::tr1::_Fnv_hash<>::hash(__s, strlen(__s)); }
  40. #endif
  41.         };
  42. }//}}}

  43. typedef std::list<std::string> string_list;
  44. typedef std::map<std::string, int> string_map;
  45. typedef __gnu_cxx::hash_map<std::string, int, zl::hash_string> string_hash_map;
  46. typedef std::tr1::unordered_map<std::string, int> string_unordered_map;

  47. void fill_list(string_list& slist, size_t count);
  48. uint64_t current_usec();

  49. int main( int argc, char* argv[] )
  50. {
  51.     if (argc != 2 && argc != 3)
  52.     {
  53.         fprintf(stderr, "Usage:%s test_count rehash\n", argv[0]);
  54.         fprintf(stderr, "For example:%s 10000 rehash\n", argv[0]);
  55.         return -1;
  56.     }

  57.     size_t count = atoi(argv[1]);
  58.     bool rehash = false;
  59.     if (argc == 3)
  60.     {
  61.         rehash = true;
  62.     }

  63.     string_map smap;
  64.     string_hash_map shash_map;
  65.     string_unordered_map sunordered_map;

  66.     if (rehash)
  67.     {
  68.         sunordered_map.rehash(count);
  69.     }

  70.     string_list slist;
  71.     fill_list(slist, count);

  72.     uint64_t start = 0;
  73.     uint64_t end = 0;

  74.     uint64_t map_insert_us = 0;
  75.     uint64_t hash_map_insert_us = 0;
  76.     uint64_t unordered_map_insert_us = 0;

  77.     uint64_t map_traverse_us = 0;
  78.     uint64_t hash_map_traverse_us = 0;
  79.     uint64_t unordered_map_traverse_us = 0;

  80.     uint64_t map_find_us = 0;
  81.     uint64_t hash_map_find_us = 0;
  82.     uint64_t unordered_map_find_us = 0;

  83.     uint64_t map_delete_us = 0;
  84.     uint64_t hash_map_delete_us = 0;
  85.     uint64_t unordered_map_delete_us = 0;



  86.     // Insert test
  87.     {//{{{
  88.         string_list::iterator it(slist.begin());
  89.         string_list::iterator ite(slist.end());

  90.         //map insert
  91.         start = current_usec();
  92.         for (int i = 0; it != ite; ++it, ++i)
  93.         {
  94.             smap[*it] = i;
  95.         }
  96.         end = current_usec();
  97.         map_insert_us = end - start;

  98.         //hash_map insert
  99.         it = slist.begin();
  100.         start = current_usec();
  101.         for (int i = 0; it != ite; ++it, ++i)
  102.         {
  103.             shash_map[*it] = i;
  104.         }
  105.         end = current_usec();
  106.         hash_map_insert_us = end - start;

  107.         //unordered_map insert
  108.         it = slist.begin();
  109.         start = current_usec();
  110.         for (int i = 0; it != ite; ++it, ++i)
  111.         {
  112.             shash_map[*it] = i;
  113.         }
  114.         end = current_usec();
  115.         unordered_map_insert_us = end - start;
  116.     }//}}}

  117.     // Traverse test
  118.     {//{{{
  119.         //map traverse 
  120.         {
  121.             string_map::iterator it(smap.begin());
  122.             string_map::iterator ite(smap.end());
  123.             start = current_usec();
  124.             for (int i = 0; it != ite; ++it)
  125.             {
  126.                 i++;
  127.             }
  128.             end = current_usec();
  129.             map_traverse_us = end - start;
  130.         }

  131.         //hash_map traverse 
  132.         {
  133.             string_hash_map::iterator it(shash_map.begin());
  134.             string_hash_map::iterator ite(shash_map.end());
  135.             start = current_usec();
  136.             for (int i = 0; it != ite; ++it)
  137.             {
  138.                 i++;
  139.             }
  140.             end = current_usec();
  141.             hash_map_traverse_us = end - start;
  142.         }

  143.         //unordered_map traverse 
  144.         {
  145.             string_unordered_map::iterator it(sunordered_map.begin());
  146.             string_unordered_map::iterator ite(sunordered_map.end());
  147.             start = current_usec();
  148.             for (int i = 0; it != ite; ++it)
  149.             {
  150.                 i++;
  151.             }
  152.             end = current_usec();
  153.             unordered_map_traverse_us = end - start;
  154.         }
  155.     }//}}}

  156.     // Find test
  157.     {//{{{
  158.         string_list::iterator it(slist.begin());
  159.         string_list::iterator ite(slist.end());

  160.         //map find
  161.         start = current_usec();
  162.         for (int i = 0; it != ite; ++it, ++i)
  163.         {
  164.             smap[*it] = i;
  165.         }
  166.         end = current_usec();
  167.         map_find_us = end - start;

  168.         //hash_map find
  169.         it = slist.begin();
  170.         start = current_usec();
  171.         for (int i = 0; it != ite; ++it, ++i)
  172.         {
  173.             shash_map[*it] = i;
  174.         }
  175.         end = current_usec();
  176.         hash_map_find_us = end - start;

  177.         //unordered_map find
  178.         it = slist.begin();
  179.         start = current_usec();
  180.         for (int i = 0; it != ite; ++it, ++i)
  181.         {
  182.             shash_map[*it] = i;
  183.         }
  184.         end = current_usec();
  185.         unordered_map_find_us = end - start;
  186.     }//}}}

  187.     // Delete test
  188.     {//{{{
  189.         string_list::iterator it(slist.begin());
  190.         string_list::iterator ite(slist.end());

  191.         //map delete
  192.         start = current_usec();
  193.         for (int i = 0; it != ite; ++it, ++i)
  194.         {
  195.             smap.erase(*it);
  196.         }
  197.         end = current_usec();
  198.         map_delete_us = end - start;

  199.         //hash_map delete
  200.         it = slist.begin();
  201.         start = current_usec();
  202.         for (int i = 0; it != ite; ++it, ++i)
  203.         {
  204.             shash_map.erase(*it);
  205.         }
  206.         end = current_usec();
  207.         hash_map_delete_us = end - start;

  208.         //unordered_map delete
  209.         it = slist.begin();
  210.         start = current_usec();
  211.         for (int i = 0; it != ite; ++it, ++i)
  212.         {
  213.             shash_map.erase(*it);
  214.         }
  215.         end = current_usec();
  216.         unordered_map_delete_us = end - start;
  217.     }//}}}

  218.     //stat output
  219.     std::cout << " insert, count " << count << std::endl;
  220.     std::cout << " std::map " << map_insert_us << " us\n";
  221.     std::cout << " std::ext/hash_map " << hash_map_insert_us << " us\n";
  222.     std::cout << "std::tr1::unordered_map " << unordered_map_insert_us << " us\n";

  223.     std::cout << "\n";
  224.     std::cout << " traverse, count " << count << std::endl;
  225.     std::cout << " std::map " << map_traverse_us << " us\n";
  226.     std::cout << " std::ext/hash_map " << hash_map_traverse_us << " us\n";
  227.     std::cout << "std::tr1::unordered_map " << unordered_map_traverse_us << " us\n";

  228.     std::cout << "\n";
  229.     std::cout << " find, count " << count << std::endl;
  230.     std::cout << " std::map " << map_find_us << " us\n";
  231.     std::cout << " std::ext/hash_map " << hash_map_find_us << " us\n";
  232.     std::cout << "std::tr1::unordered_map " << unordered_map_find_us << " us\n";

  233.     std::cout << "\n";
  234.     std::cout << " delete, count " << count << std::endl;
  235.     std::cout << " std::map " << map_delete_us << " us\n";
  236.     std::cout << " std::ext/hash_map " << hash_map_delete_us << " us\n";
  237.     std::cout << "std::tr1::unordered_map " << unordered_map_delete_us << " us\n";

  238.      
  239.     return 0;
  240. }

  241. void fill_list(string_list& slist, size_t count)
  242. {
  243.     for (size_t i = 0; i < count; ++i)
  244.     {
  245.         std::ostringstream oss;
  246.         oss << i;
  247.         //slist.push_back(MD5::getHexMD5(oss.str().c_str(), oss.str().length()));
  248.         slist.push_back(oss.str());
  249.     }
  250. }


  251. uint64_t current_usec()
  252. {
  253.     struct timeval tv;
  254.     gettimeofday( &tv, NULL );
  255.     return tv.tv_sec * 1000 * 1000 + tv.tv_usec;
  256. }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章