https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92300

--- Comment #6 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jonathan Wakely <r...@gcc.gnu.org>:

https://gcc.gnu.org/g:db5fa0837e464b595a3d63766060bae1c9ac5ccc

commit r12-5876-gdb5fa0837e464b595a3d63766060bae1c9ac5ccc
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Fri Dec 3 14:33:13 2021 +0000

    libstdc++: Avoid unnecessary allocations in std::map insertions [PR92300]

    Inserting a pair<Key, Value> into a map<Key, Value> will allocate a new
    node and construct a pair<const Key, Value> in the node, then check if
    the Key is already present in the map. That is because pair<Key, Value>
    is not the same type as the map's value_type. But it only differs in the
    const-qualification on the Key, and so we should be able to do the
    lookup directly, without allocating a new node. This avoids allocating
    and then deallocating a node for the case where the key is already found
    and nothing gets inserted.

    We can take this optimization further and lookup the key directly for a
    pair<Key, X>, pair<const Key, X>, pair<Key&, X> etc. for any X. A strict
    reading of the standard says we can only do this when we know the
    allocator won't do anything funky with the value when constructing a
    pair<const Key, Value> from a slightly different type. Inserting that
    type only requires the value_type to be Cpp17EmplaceInsertable into the
    container, and that doesn't have any requirement that the value is
    unchanged (unlike Cpp17CopyInsertable and Cpp17MoveInsertable). For that
    reason, the optimization is only done for maps using std::allocator.

    A similar optimization can be done for map.emplace(key, value) where the
    first argument is similar to the key_type and so can be looked up
    without allocating a new node and constructing a key_type.

    Finally, both of the insert and emplace cases can use the same
    optimization when key_type is a scalar type and some other scalar is
    being passed as the insert/emplace argument. Converting from one scalar
    type to another won't have surprising value-altering behaviour, and has
    no side effects (unlike e.g. constructing a std::string from a const
    char* argument, which might allocate).

    We don't need to do this for std::multimap, because we always insert the
    new node even if the key is already present. So there's no benefit to
    doing the lookup before allocating the new node.

    libstdc++-v3/ChangeLog:

            PR libstdc++/92300
            * include/bits/stl_map.h (insert(Pair&&), emplace(Args&&...)):
            Check whether the arguments can be looked up directly without
            constructing a temporary node first.
            * include/bits/stl_pair.h (__is_pair): Move to here, from ...
            * include/bits/uses_allocator_args.h (__is_pair): ... here.
            * testsuite/23_containers/map/modifiers/emplace/92300.cc: New test.
            * testsuite/23_containers/map/modifiers/insert/92300.cc: New test.

Reply via email to