Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pre-allocating buckets in a C++ std::unordered_map

I'm using the std::unordered_map from gnu++0x to store a huge amount of data. I want to pre-allocate space for the large number of elements, since I can bound the total space used.

What I would like to be able to do is call:

std::unordered_map m;
m.resize(pow(2,x));

where x is known.

std::unordered_map doesn't support this. I would rather use std::unordered_map if possible, since it will eventually be part of the standard.

Some other constraints:

Need reliable O(1) access and mutation of the map. The desired hash and comparison functions are already non-standard and somewhat expensive. O(log n) mutation (as with std::map) is too expensive.

-> The expensive hash and comparison also make amortization-based growth way too expensive. Each extra insert requires O(n) operations from those functions, which results in an extra quadratic term in the algorithm's run time, since the exponential storage requirements need O(n) growths.

like image 851
JAD Avatar asked May 05 '11 23:05

JAD


People also ask

What is a bucket in Unordered_map?

C++ Unordered_map Library - bucket() Function Bucket is a memory space in the container's hash table to which elements are assigned based on the hash value of their key. Valid range of buckets is from 0 to bucket_count - 1.

How are elements stored in Unordered_map?

unordered_map is an associated container that stores elements formed by the combination of a key value and a mapped value. The key value is used to uniquely identify the element and the mapped value is the content associated with the key. Both key and value can be of any type predefined or user-defined.

How is std :: Unordered_set implemented?

An unordered_set is implemented using a hash table where keys are hashed into indices of a hash table so that the insertion is always randomized.


3 Answers

m.rehash(pow(2,x)); 

if pow(2, x) is the number of buckets you want preallocated. You can also:

m.reserve(pow(2,x)); 

but now pow(2, x) is the number of elements you are planning on inserting. Both functions do nothing but preallocate buckets. They don't insert any elements. And they are both meant to be used exactly for your use case.

Note: You aren't guaranteed to get exactly pow(2, x) buckets. Some implementations will use only a number of buckets which is a power of 2. Other implementations will use only a prime number of buckets. Still others will use only a subset of primes for the number of buckets. But in any case, the implementation should accept your hint at the number of buckets you desire, and then internally round up to its next acceptable number of buckets.

Here is the precise wording that the latest (N4660) uses to specify the argument to rehash:

a.rehash(n) : Postconditions: a.bucket_count() >= a.size() / a.max_load_factor() and a.bucket_count() >= n.

This postcondition ensures that bucket()_count() >= n, and that load_factor() remains less than or equal to max_load_factor().

Subsequently reserve(n) is defined in terms of rehash(n):

a.reserve(n) : Same as a.rehash(ceil(n / a.max_load_factor())).

like image 183
Howard Hinnant Avatar answered Oct 11 '22 20:10

Howard Hinnant


I don't think it matters for an unordered map to have pre-allocated memory. The STL is expected to be O(n) amortized insertion time. Save yourself the hassle of writing your own allocator until you know this is the bottle neck of your code, in my opinion.

like image 20
Mike Lyons Avatar answered Oct 11 '22 18:10

Mike Lyons


I would suggest writing your own allocator for the std::unordered_map that allocates memory exactly in the way you want.

like image 44
orlp Avatar answered Oct 11 '22 18:10

orlp