std::*map as fast as possible

Abhinav Agarwal
2 min readDec 21, 2020

The views presented here are solely based author’s personal opinion, research, observations and are in no way affiliated to any organization.

std::map offers an associative container that stores unique key-value pairs sorted by keys. It is implemented using red-black trees and offers O(log n) time complexity for insertion, deletion, and find. For data that need not have any particular ordering, std::unordered_map can provide O(1) amortized complexity for insertion, deletion, and find.

Map containers are widely used in code, but a few important details in map insertion are often overlooked which adds an unnecessary performance penalty.

Consider a typicalstd::unordered_map usage:

std::unordered_map<K, V> my_map;

Now to insert in this map, one may use insert function provided by std::unordered_map as:

my_map.insert(std::make_pair(key,
V(arg1, arg2)));

Here, many unnecessary temporary copies are being created. One of the least suspecting copies may be due to conversion of outer std::make_pair. insert function expects std::pair<const Key, Value>, so unless key object is const, there would be an additional implicit conversion (and hence the copy).

More experienced C++ developers will immediately suggest using emplace instead of insert, to leverage perfect forwarding. Using emplace , removes outer std::make_pair.

my_map.emplace(key, V(arg1, arg2));

Interestingly, if V does not have a copy (or move) constructor, the above line won't even compile. Contrast this with typical usage of std::vector.

std::vector<V> vals;
vals.emplace_back(arg1, arg2);

In a std::vector<V>, we are able to construct V in-place using emplace_back function call. So the question is, how to do it in a std::map?

Let’s look at the definition of map:

template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
>
class map;

and the definition emplace call:

template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );

It’s clear that emplace just forwards the arguments. So we actually want to construct the Value in the map in-place. Recall that std::map stores items as std::pair. Looking at the constructor of std::pair, we find:

template< class... Args1, class... Args2 >
pair( std::piecewise_construct_t,
std::tuple<Args1...> first_args,
std::tuple<Args2...> second_args );

std::piecewise_construct_t is the trick to constructing the items of pair in-place. With this, we can do emplace in std::map, without creating any temporary copies as follows:

my_map.emplace(std::piecewise_construct_t,
std::forward_as_tuple(key),
std::forward_as_tuple(arg1, arg2));

std::forward_as_tuple will bind the template arguments to key and value types as necessary to construct them individually in-place.

Interesting read:

  1. N4387 — Improving pair and tuple

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Abhinav Agarwal
Abhinav Agarwal

Written by Abhinav Agarwal

I have a particular interest in developing high-performance systems which require in-depth knowledge of computer systems and networks.

No responses yet

Write a response