8

With g++ 5.4 compiler and GNU standard library libstdc++.so.6, default constructor of std::vector creates an empty container initializing only internal bookkeeping data members on stack. It allocates buffer on heap for data elements later (when the fist element is inserted). Until recently, I thought it was a common behavior for any standard sequential container with dynamically allocated memory.

However, std::deque works differently. Tracing the following code

#include <deque> 
int main() {
    std::deque<int> d;
    return 0;
}

with ltrace gives

__libc_start_main(0x4024fa, 1, 0x7ffd088be0f8, 0x405bd0 <unfinished ...>
_ZNSt8ios_base4InitC1Ev(0x608351, 0xffff, 0x7ffd088be108, 160)        = 0
__cxa_atexit(0x401f20, 0x608351, 0x608210, 0x7ffd088bded0)            = 0
_Znwm(64, 8, 0, 8)                                                    = 0x7b4c20
_Znwm(512, 128, 0, 128)                                               = 0x7b4c70
_ZdlPv(0x7b4c70, 0x7b4c70, 128, 0x7b4c70)                             = 1
_ZdlPv(0x7b4c20, 0x7b4c20, 8, 0x7b4c20)                               = 0
_ZNSt8ios_base4InitD1Ev(0x608351, 0, 0x401f20, 0x7fc6d4567d10)        = 0x7fc6d4b00880
+++ exited (status 0) +++

_Znwm is an implementation of operator new (please see this). Considering the internal structure of std::deque and the #define _GLIBCXX_DEQUE_BUF_SIZE 512 line from STL implementation header bits/stl_deque.h, one may conclude that _Znwm(64, 8, 0, 8) allocates std::deque's map for 8 pointers and _Znwm(512, 128, 0, 128) allocates one chunk of 512 bytes.

The question is: what is the reason not to postpone allocating these 512 bytes until the first element in std::deque is inserted?

Community
  • 1
  • 1
ktrushin
  • 81
  • 4
  • 3
    Are you sure your leading statements are correct as per the C++ standard? – juanchopanza Oct 23 '16 at 13:33
  • I described my implementation specifically. – ktrushin Oct 23 '16 at 13:51
  • Maybe you can add that information to your question. – juanchopanza Oct 23 '16 at 13:52
  • Added requested information to the very beginning of the question. – ktrushin Oct 23 '16 at 13:55
  • added standard library as well – ktrushin Oct 23 '16 at 14:02
  • 1
    I think deferring the memory allocation also has its pros and cons. If the memory allocation is deferred till first element is inserted, then it means if there is an insertion in the critical path, latency introduced in memory allocation can't be avoided as `deque::reserve()` doesn't exist (while `vector::reserve()` does). But if the initial memory allocation is done when `deque` is constructed, the programmer can create the `deque` outside the critical path. – HCSF Jul 07 '19 at 07:02
  • @HCSF, sorry for the late response, but I owe you a debt of gratitude for your comment. It sheds light on the rationale behind the design decisions made in `deque` implementation. It was exactly I wanted to know what I created the question almost three years ago. – ktrushin Aug 02 '19 at 14:21

1 Answers1

2

The most likely reason is that if the implementation did not do, every push_back(), push_front(), begin(), end(), insert() would have to check to see if the block of pointers to pages is allocated, and if not execute allocation of the block of pointers + the first page. That conditional checking slows these operations down, and the number of these operations is likely to dwarf the instances of a deque constructor executing.

The implementation has decided to stump up a minimum block of pointers + possibly an empty page. Effectively it is like a sentinel.

Would you really rather these common member functions be slower?

SJHowe
  • 756
  • 5
  • 11
  • 2
    Anyway, `push_back()` and other member functions you mentioned have to check that the respective chunk (page in your terminology) has free space to place the element being added and allocate another chunk if necessary. From my point of view, the checking itself doesn't slow down the member functions significantly. It is memory allocated which does. Providing that we can't avoid memory allocation in the mentioned member functions, adding yet another one wouldn't change the situation considerably. Please correct me if I'm wrong. – ktrushin Oct 24 '16 at 08:26