0

Take the following code and compile it with -O3 under any compiler out there: msvc, clang, gcc.

#include <map>
#include <unordered_map>
#include <vector>

int main() {
    std::map< int, int > m;
    std::map< int, int > p;
    std::map< int, int > q;
    std::unordered_map< int, int > r;
    std::vector<int> n;
}

It generates assembly that contains one call for each of the std::map but no assembly whatsoever for std::unordered_map or std::vector. You can comment out those lines and there will be no difference in the resulting assembly. If you comment any of the lines with std::map though there will be a reduction.

     leaq    104(%rsp), %rdi
        xorl    %esi, %esi
        callq   std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase(std::_Rb_tree_node<std::pair<int const, int> >*)
        movq    24(%rsp), %rsi
        leaq    8(%rsp), %rdi
        callq   std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase(std::_Rb_tree_node<std::pair<int const, int> >*)
        movq    72(%rsp), %rsi
        leaq    56(%rsp), %rdi
        callq   std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase(std::_Rb_tree_node<std::pair<int const, int> >*)
        xorl    %eax, %eax
        addq    $152, %rsp
        retq
        movq    %rax, %rdi
        callq   __clang_call_terminate
        movq    %rax, %rdi
        callq   __clang_call_terminate
        movq    %rax, %rdi
        callq   __clang_call_terminate
     leaq    104(%rsp), %rdi
        xorl    %esi, %esi
        callq   std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase(std::_Rb_tree_node<std::pair<int const, int> >*)
        movq    24(%rsp), %rsi
        leaq    8(%rsp), %rdi
        callq   std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase(std::_Rb_tree_node<std::pair<int const, int> >*)
        movq    72(%rsp), %rsi
        leaq    56(%rsp), %rdi
        callq   std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase(std::_Rb_tree_node<std::pair<int const, int> >*)
        xorl    %eax, %eax
        addq    $152, %rsp
        retq
        movq    %rax, %rdi
        callq   __clang_call_terminate
        movq    %rax, %rdi
        callq   __clang_call_terminate
        movq    %rax, %rdi
        callq   __clang_call_terminate

What is the reason for this? Why isn't any of the compilers able to optimize away any of the std::map objects, although none of them has any side effect?

Godbolt: https://godbolt.org/z/cjfE3hjfo

  • Clang does a bit better with libc++, but it's not completely gone. – chris Dec 13 '21 at 00:50
  • 1
    All kinds of maps have side effects, they all may throw exceptions. Underlying object in std::map is B-tree, perhaps clang could not optimize empty trees. std::unordered_map has array empty by default (not allocated), and could be optimized. Empty std::vector has the explicit noexcept constructor, thus has no side-effect. – 273K Dec 13 '21 at 01:03
  • 2
    ralated: https://stackoverflow.com/questions/34590885/optimization-of-raw-new-delete-vs-stdvector The accepted answer has a lot of good insights. – bolov Dec 13 '21 at 01:09

0 Answers0