Isaac D. Cohen
Isaac D. Cohen

Reputation: 881

How much memory does an initial empty unordered_map use?

If I declare a hashmap like this:

std::unordered_map <int, int> m;

before I put anything in, how much memory is actually allocated for the map?

Upvotes: 3

Views: 945

Answers (1)

Tony Delroy
Tony Delroy

Reputation: 106096

It's implementation defined, but you can observe the memory usage for any given implementation you're using with a combination of sizeof (for the unordered_map object itself), and a custom allocator policy that documents dynamic allocation requests.

I coded that up on godbolt - https://godbolt.org/z/eoGfvv4zf - just writing as minimal a custom allocator as proved necessary to get it to run under the newest versions of gcc, clang and msvc. The code's below too for reference.

As of 2022/12/29, it shows sizeof 64 for the gcc 12 and clang 15, which don't ask the allocator for extra memory until an insertion is done, while msvc v19 has sizeof 40 and does ask the allocator for extra memory even when empty, with 80 bytes allocated (plus any management overheads the memory allocation library might have):

allocate(1 struct std::_List_node<struct std::pair<int const ,int>,void *> == 16 bytes) allocate(16 class std::_List_unchecked_iterator<class std::_List_val<struct std::_List_simple_types<struct std::pair<int const ,int> > > > == 64 bytes)

#include <unordered_map>
#include <iostream>
#include <utility>

template <typename T>
struct my_allocator {
    my_allocator() = default;
    template< class U >
    constexpr my_allocator( const my_allocator<U>& other ) noexcept { }
    using Base = std::allocator<T>;
    Base base;
    using value_type = typename Base::value_type;
    T* allocate(std::size_t n, const void* hint) {
        return allocate(n);
    }
    [[nodiscard]] constexpr T* allocate(std::size_t n) {
        std::cout << "allocate(" << n << ' ' << typeid(T).name()
        << " == " << n * sizeof(T) << " bytes)\n";
        return base.allocate(n);
    }
    constexpr void deallocate(T* p, std::size_t n) {
        base.deallocate(p, n);
    }
};

int main() {
    std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, my_allocator<std::pair<const int, int>>> m;
    std::cout << "sizeof " << sizeof m << '\n';
    for (int i = 0; i < 1; ++i)
        m[i] = i;
    std::cout << "buckets: " << m.bucket_count() << '\n';
}

Upvotes: 4

Related Questions