![](/img/trans.png)
[英]Fast construction of unordered_map with nested vector with preallocation std::unordered_map<int, vector<Thing *>>?
[英]unordered_map vs vector with int keys
當鍵是整數時,使用std::unordered_map
或std::vector
在性能上有何不同。 我有大約 100-1000 個具有連續 ID 的元素,我可以使用它們來訪問向量。 使用 hash 表的原因是通過對象本身進行索引更方便。
想象三種不同的情況:
請注意,我將其作為一般性問題提出,而不是作為特定於代碼的問題。
通常來說,一般來說。
如果你有100-1000
元素,容器本身並不重要——使用std::map
甚至比std::unordered_map
更好,例如,如果你需要調試打印內容——除非你以某種方式依賴在哈希上。 當您獲得 10 萬多個元素時,容器性能開始變得有趣。
一般來說:
由於缺乏間接性,Vector 具有更好的緩存一致性。 訪問索引更快,因為不需要計算散列函數。 迭代具有可預測的分支。
無序映射使用稀疏結構使用更少的內存(你說索引是連續的,所以這個優勢不適用於你)。 使用無序映射在任意索引中添加或刪除元素的速度越來越快。
當您只有很少的元素(例如 100-1000)時,漸近復雜性不一定重要。 在這種情況下,緩存一致性和分支預測往往占主導地位。
首先選擇哪種數據結構更方便。 然后衡量訪問該結構是否對整個程序的性能產生重大影響。 如果是,則測量與其他數據結構的差異,看看它是否明顯更快(與測量方差有關)。
vector
與map
在處理鍵的方式上存在重大差異。 忽略性能,並在刪除節點時使用對密鑰具有正確行為的任何一個。
如果您的項目少於 ~1000 個,那么性能並不重要。
我只是想知道同樣的問題。 雖然我通常同意在大多數情況下應該使用最方便的容器,並且當有疑問時,您應該自己測量,但我認為至少對我們正在談論的尺寸有一個大概的了解是很好的。
因此,我實現了一個小例子:
#include <string>
#include <iostream>
#include <vector>
#include <unordered_map>
#include <chrono>
struct MyStruct {
int value;
};
int main(int argc, char** argv) {
size_t const SIZE = 100;
size_t const ACCESSES = 10000;
std::vector<MyStruct const*> my_structs;
for (size_t i = 0; i < SIZE; ++i) {
my_structs.push_back(new MyStruct());
}
std::vector<double> values_vec(my_structs.size());
std::unordered_map<MyStruct const*, double> values_map;
for (size_t i = 0; i < SIZE; ++i) {
double rand_val = (rand() % 1000)/10;
values_vec[i] = rand_val;
values_map[my_structs[i]] = rand_val;
}
std::vector<MyStruct const*> queries_ptr;
std::vector<size_t> queries_int;
for (size_t i = 0; i < ACCESSES; ++i) {
size_t idx = rand() % SIZE;
queries_int.push_back(idx);
queries_ptr.push_back(my_structs[idx]);
}
auto begin_vec = std::chrono::steady_clock::now();
double large_sum_vec = 0;
for (size_t query : queries_int) {
large_sum_vec += values_vec[query];
}
auto end_vec = std::chrono::steady_clock::now();
double large_sum_map = 0;
for (MyStruct const* query : queries_ptr) {
large_sum_map += values_map[query];
}
auto end_map = std::chrono::steady_clock::now();
std::cout << "Results for " << ACCESSES << " accesses to vector of size " << SIZE << std::endl;
std::cout << "=== VEC === Result = " << large_sum_vec << " Time = " << std::chrono::duration_cast<std::chrono::microseconds>(end_vec - begin_vec).count() << " microseconds" << std::endl;
std::cout << "=== MAP === Result = " << large_sum_vec << " Time = " << std::chrono::duration_cast<std::chrono::microseconds>(end_map - end_vec).count() << " microseconds" << std::endl;
for (size_t i = 0; i < SIZE; ++i) {
delete my_structs[i];
}
}
您可以在 Coliru 上找到它: https://coliru.stacked-crooked.com/a/a986dd2607a8566a
結果:帶索引的 std::vector 比隨機讀取訪問的 std::unordered_map 快大約 10-20 倍
有趣的是,兩種運行時間幾乎都與數據大小無關。 隨着訪問次數的增加,該關系更趨向於 1:20。 我沒有測試寫密集型/混合型,但我不認為會有太大差異,因為此時已經找到了元素。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.