簡體   English   中英

C++ 比較預先保留的哈希映射(std::unordered_map)與整數鍵和連續數據數組(std::vector)

[英]C++ comparing a pre-reserved hash map(std::unordered_map) with integer key and contiguous data array(std::vector)

假設使用具有int鍵類型的哈希映射結構:

std::unordered_map<int, data_type> um;

另外,當元素的總數(或最大)數N已知時,可以提前構建哈希表。

um.reserve(N); // This will chainly call rehash() function...

在這里,據我所知,整數本身可以用作哈希表的身份(哈希)函數

同時,對於連續數據集(即std::vector或簡單數組),可以通過從最前面數據的地址位移來隨機訪問它。

兩個容器都使用int作為訪問密鑰,如下所示:

um[1] = data_type(1); //std::unordered_map<int, data_type>
v[1] = data_type(1); //std::vector<data_type>

那么,構造的哈希表和std::vector在內存使用或搜索機制/性能或其他方面有什么區別嗎?

讓我們把問題具體化。

如果我知道0 , 5 , 9987 3個鍵肯定會使用,但鍵1 ~ 9986可能會也可能不會使用。

如果我知道集合中沒有鍵大於10000 ,那么使用大小為10000 std::vector將保證訪問隨機數據的時間復雜度為 O(1),但會浪費內存。

在這種情況下, std::unordered_map是否為問題提供了更好的解決方案? *我的意思是,一種在將時間復雜度保持在同一級別的同時盡可能多地節省內存的解決方案。

一切都不一樣了。

unordered_map 有的概念 -

存儲桶是容器內部哈希表中的一個槽,元素根據其鍵的哈希值分配到該槽。 桶的編號從 0 到 (bucket_count-1)。

unordered_map 計算指向桶的鍵的哈希值。 所需的值在該存儲桶中。 現在請注意,多個鍵可以指向單個存儲桶。 在您的情況下,甚至可能發生um[0]um[5]um[9987]都在同一個桶中! 桶內搜索在時間上是線性的。

在這種情況下, std::unordered_map 是否為問題提供了更好的解決方案?

如果您有稀疏數據,請使用 unordered_map 但具有適當的保留(或根本沒有保留並使用默認分配策略)。 如果您執行myMap.reserve(MAX_ELEMENTS)則毫無意義,因為這將再次導致內存浪費。

否則,使用向量。 您將獲得有保證的O(1)查找。 由於它是線性的,它對緩存非常友好。 而在 unordered_map 上,您可能會得到O(N)的最壞情況查找

另外,當元素的總數(或最大)數 N 已知時,可以提前構建哈希表。

嗯.reserve(N); // 這將鏈式調用 rehash() 函數...

在這里,據我所知,整數本身可以用作哈希表的身份(哈希)函數。

這是真的,並且在兩種非常不同的情況下是合理的:1) 當這些值幾乎與一些缺失值相鄰時,或者 2) 當這些值非常隨機時。 在許多其他情況下,如果您不提供有意義的哈希函數,您可能會面臨過度哈希表沖突的風險。

那么,構造的哈希表和 std::vector 之間在內存使用或搜索機制/性能或其他方面有什么區別嗎?

是的。 在您的.reserve(N) ,哈希表為至少N “存儲桶”分配一個連續的內存塊(基本上是一個數組)。 如果我們考慮 GCC 實現,N 將四舍五入為素數。 每個桶可以將一個迭代器存儲到一個pair<int, data_type>節點的前向鏈表中。

所以,如果你真的把 N 個條目放入哈希表中,你有......

  • >= N 個sizeof(forward-list-iterator)大小的元素的數組
  • N 個內存分配 >= sizeof(pair<int, data_type>) + sizeof(next-pointer/iterator for forward-list)

...雖然vector僅使用大約N * sizeof(data_type)個字節的內存:可能是哈希表使用的內存的一小部分,並且由於data_type s 的所有向量內存是連續的,你更有可能受益於與您當前嘗試訪問的元素相鄰的 CPU 緩存元素,以便以后訪問它們都快得多。

另一方面,如果你沒有把很多元素放入哈希表,那么使用內存的主要是包含迭代器的桶數組,它通常是指針的大小(例如每個 32 或 64 位),而data_type向量 - 如果你也reserve(N) - 將已經分配了N * sizeof(data_type)個字節的內存 - 對於可能比哈希表大得多的大型data_type s。 盡管如此,您仍然可以經常分配虛擬內存,並且如果您沒有將內存頁面錯誤地導致它們需要物理后備內存,那么您的程序或計算機就沒有有意義的內存使用或性能損失。 (至少對於 64 位程序,虛擬地址空間實際上是無限的)。

如果我知道 3 個鍵 0,5, 9987 肯定會使用,但鍵 1~9986 可能會也可能不會使用。

如果我知道集合中沒有鍵大於 10000,那么使用大小為 10000 的 std::vector 將保證訪問隨機數據的時間復雜度為 O(1),但會浪費內存。

在這種情況下, std::unordered_map 是否為問題提供了更好的解決方案? *我的意思是,一種在將時間復雜度保持在同一級別的同時盡可能多地節省內存的解決方案。

在這種情況下,如果您reversed(10000)並且data_type沒有明顯大於迭代器/指針,那么unordered_map在各個方面都會明顯更糟。 如果您不預先保留,哈希表只會為少數存儲桶分配空間,並且您使用的虛擬地址空間將比具有 10000 個元素的vector量少得多(即使data_typebool )。

如果你只有 3 個元素要打包,最好的解決方案是使用std::vector<std::pair<int, data_type>> :) 它比std::unordered_map<int, data_type>占用的內存更少(實際上分配幾個向量桶),並且由於常量非常小,查找性能對於少量元素也是最好的。

對於較大的地圖, O(1)復雜度由std::vector<data_type>std::unordered_map<int, data_type> ,但對於向量,隱藏在O的常數要低得多,因為它沒有需要根據桶中的其他元素檢查元素。 我建議總是更喜歡 vector ,除非你缺乏適合它的內存,在這種情況下,你可以通過犧牲一些性能來使用unordered_map來節省內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM