Reputation: 3585
I have an unordered map, exactly like:
std::unordered_map < std::string, std::vector<std::string> > m_unordered_map;
While inserting values into it, which of the following would be faster and why?
Approach 1 (Skeleton):
std:string key = "key";
for (int i = 0; i < n; ++i) {
std::string value = std::to_string(i); // Value would be acually an big string.
m_unordered_map[key].push_back(value1);
}
Approach 2 (Skeleton)
std:string key = "key";
std::vector<std::string> vec;
for (int i = 0; i < n; ++i) {
std::string value = std::to_string(i); // Value would be acually an big string.
vec.insert(value);
}
m_unordered_map.insert({key, vec});
Or is there a better approach to do this?
Upvotes: 0
Views: 1450
Reputation: 218323
Amelioration of your 2 approaches:
Approach 1(Skeleton):
std:string key = "key";
auto& vec = m_unordered_map[key];
vec.reserve(n);
for (int i = 0; i != n; ++i) {
vec.push_back(std::to_string(i));
}
Approach 2(Skeleton)
std:string key = "key";
std::vector<std::string> vec;
vec.reserve(n);
for (int i = 0; i != n; ++i) {
vec.push_back(std::to_string(i));
}
m_unordered_map[key] = std::move(vec));
So
On my side, I would create a method to build the vector, something like:
std::vector<std::string> create_iota_strings(std::size_t n)
{
std::vector<std::string> vec;
vec.reserve(n);
for (std::size_t i = 0; i != n; ++i) {
vec.push_back(std::to_string(i));
}
return vec;
}
and then simply
m_unordered_map[key] = create_iota_strings(n);
insert
/emplace
might be more appropriate than operator[]
if you know key doesn't already exist.
If key might exist try_emplace
would be the solution to avoid to construct the empty vector (when key doesn't exist).
Upvotes: 1
Reputation: 30165
Assuming you know the key in advance for a batch of things, then the second of the versions you have is significantly better, as it avoids doing a map lookup on every iteration.
You may further improve it by moving instead of copying the string and vectors. e.g.
std::string key = "key";
std::vector<std::string> vec;
for (int i = 0; i < 10; ++i) {
vec.emplace_back(std::to_string(i));
}
m_unordered_map.emplace(key, std::move(vec));
In general:
unordered_map
is still fairly slow, especially with keys like std::string
. For a "hit" it costs you a O(n) hash on the keys length, and a O(n) string comparison. Not just O(1) for the hash table itself. If you can access it just once, maybe keep an iterator/reference that will be faster (check the iterator invalidation rules. insert
may invalidate other iterators in an unordered_map
, so be careful, but it won't invalidate references to values). If you can replace the string entirely with say an integer ID that will also normally be faster.Upvotes: 4