STL Source Analysis (1): Space Configurator

Source: Internet
Author: User

All STL operands (all values) are stored inside the container, and the container needs to allocate space to hold the data. Why not say allocator is a memory configurator but a space configurator, because space is not just memory, it can also be disk or other secondary storage media. Here we mainly discuss memory configuration.
SGI STL The default space configurator for each container is alloc, such as vector:

    template<classclass Alloc = alloc>     classvector{……}

In general, C + + 's memory configuration and release operations are as follows:

    classObject{……};    ObjectnewObject();    delete ob;

Here new actually contains two operations, the previous article also mentions:
1) Allocating space
2) Call construct Hanshu
Delete also includes two actions:
1) Call destructor
2) Free space

Let's take a look at the code of construction and destruction first
STL allocator also has a corresponding two operations, memory allocation by alloc::allocate () is responsible, released by Alloc::d Eallocate () is responsible for, constructs by:: Construct () responsible, destructor by::d Estroy ().

Template<classT>inline voidDestroy (t* pointer) {pointer->~t ();}Template<classT1,classT2>inline voidConstruct (t1* p,Constt2& value) {New(p) T1 (value);}Template<classForwarditerator>inline void__destroy_aux (ForwardIterator First, ForwardIterator last, __false_type) { for(; first < last; ++first) destroy (&*first);}Template<classForwarditerator>inline void__destroy_aux (ForwardIterator, ForwardIterator, __true_type) {}Template<classForwardIterator,classT>inline void__destroy (ForwardIterator First, ForwardIterator last, t*) {typedef TypeName__type_traits<t>::has_trivial_destructor Trivial_destructor; __destroy_aux (First, Last, Trivial_destructor ());}Template<classForwarditerator>inline voidDestroy (ForwardIterator first, ForwardIterator last) {__destroy (First, Last, Value_type (first));}

Here, New (p) T1 (value) is actually a placement new, and the constructor is called in the existing space.
Destroy () has two versions, and the first version accepts an object pointer to deconstruct a single object.
The second version of Destroy (), which is a destructor for all objects in the two iterator interval.
Value_type () is also a function template, its main function is to use the compiler type derivation, get the type of the iterator, and then use the property extraction type of the iterator to get the embedded type value_type of the iterator. The compiler's type derivation is used to obtain the Value_type inline type of the iterator in the __destroy () function.
Look at the code:

template <class Iterator>inlinetypename iterator_traits<Iterator>::value_type*value_type(const Iterator&) {  returnstatic_cast<typename iterator_traits<Iterator>::value_type*>(0);}

For __type_traits::has_trivial_destructor Trivial_destructor, different data types return different types:
For example, int is this:

__STL_TEMPLATE_NULL struct __type_traits<int> {   typedef __true_type    has_trivial_default_constructor;   typedef __true_type    has_trivial_copy_constructor;   typedef __true_type    has_trivial_assignment_operator;   typedef __true_type    has_trivial_destructor;   typedef __true_type    is_POD_type;};

This type can be used to know whether the destructor is irrelevant, if not, and if not, iterate over the interval and call the destructor.

The

is configured in SGI STL into two-level, first-level configurator and second-level configurator. The
first-level Configurator uses malloc () and free () directly, and the second-level Configurator takes different policies depending on the situation. For a configuration area greater than 128byte, considered large enough to call the first-level configurator directly, for a memory allocation request less than 128byte, we use a second-level memory configurator. The second-level memory Configurator is a pool of memory with a total of 16 allocated chunks formed by the linked list. The size of these 16 linked lists is 8,16,24....128byte, which is a multiple of 8. Each request is less than or equal to 128byte, the size of the request is raised to the nearest multiple of 8, for example, 7 will be raised to 8,30 up to 32, and then find the corresponding size of the list of chunks, from which to remove a chunk back to the request.
The advantage of using a memory pool for a second-level configurator is to avoid memory fragmentation caused by too many small chunks. At the same time, each allocation of memory needs to call malloc to allocate, the time spent on malloc calls and other resources are certain, for large chunks of the allocation of such resources such as the consumption of time, there is nothing. But for small chunks, its consumption is too worthless. We can use a pre-allocated a large block of contiguous, string it into a fixed-size blockchain list, (8 times the number of nodes), the next time, from the corresponding pre-allocated list of the blockchain to find a size, the nearest block directly returned to the request, This avoids the malloc call to the cell block. At the same time for the cell block release, you can directly add it to the memory pool in the corresponding size of the linked list. The
first-level Configurator is this, which is simple:

static void * allocate(size_t n){    void *result = malloc(n);    if (0resultresult = oom_malloc(n);    returnresult;}

malloc () is called First, and Oom_malloc () is not successfully called:

template  <int  inst>void  * __malloc_alloc_template<inst>::o    Om_malloc (size_t N) {void  (* my_malloc_handler) ();    void  *result; for  (;;)        {My_malloc_handler = __malloc_alloc_oom_handler; if         (0  = = My_malloc_handler) {__throw_bad_alloc;} (*my_malloc_handler)        ();        result = malloc  (n); if  (Result) return     (result); }}

In the For loop, call My_malloc_handler (), which is set with Set_malloc_handler () (This is what, poke here: Set_new_handler () summary), if 0, throw _bad_alloc, Otherwise it will be assigned until it succeeds, yes, that's it.
Let's look at the second level configurator:
Maintain a free_list[16] array in the second-level configurator, which stores the 8,16,24,32 ... 128byte The first node address of a list of various size chunks. The FREE_LIST node structure is as follows:

  union obj {        union obj * free_list_link;        char client_data[1];    /* The client sees this.        */  };

It's a good way to save space and not maintain the memory wasted by the list pointers, you see.

No more wordy, just look at the code:

  / * n must be > 0 * /  Static void* Allocate (size_t n) {obj * __volatile * my_free_list; obj * __restrict result;if(n > (size_t) __max_bytes) {//greater than 128byte, direct malloc ()        return(Malloc_alloc::allocate (n)); } my_free_list = Free_list + freelist_index (n);////Find List of affiliated links    //Acquire the lock here with a constructor call.    //This ensures the It is released in exit or during stack    //unwinding.# ifndef _nothreads        /*referenced*/        LockLock_instance;# endif result = *my_free_list;if(Result = =0) {//No free space        void*r = Refill (ROUND_UP (n));//Request Assignment        returnR } *my_free_list = result--Free_list_link;//point to next free space address    return(result); };/* p may not be 0 * /  Static voidDEALLOCATE (void*p, size_t N) {obj *q = (obj *) p; obj * __volatile * my_free_list;if(n > (size_t) __max_bytes) {//greater than 128byte, direct free ()Malloc_alloc::d eallocate (P, N);return; } my_free_list = Free_list + freelist_index (n);//Find a list of affiliated links    //Acquire lock# ifndef _nothreads        /*referenced*/        LockLock_instance;# endif /* _nothreads * *Q-free_list_link = *my_free_list;//Insert the space into the list header*my_free_list = q;//Lock is released here}/ * We allocate memory in large chunks in order to avoid fragmenting * // * The malloc heap too much. *// * We assume that size is properly aligned. *// * We hold the allocation lock. */Template <BOOLThreadsintInst>Char*__default_alloc_template<threads, Inst>::chunk_alloc (size_t size,int& Nobjs) {Char* result; size_t total_bytes = size * NOBJS;//Required Spacesize_t bytes_left = End_free-start_free;//Memory pool space left    if(Bytes_left >= total_bytes) {//There is enough space leftresult = Start_free; Start_free + = Total_bytes;return(result); }Else if(Bytes_left >= size)//The remaining space is more than one, less than 20NOBJS = bytes_left/size;        total_bytes = size * NOBJS;        result = Start_free; Start_free + = Total_bytes;return(result); }Else{//Neither one is enoughsize_t Bytes_to_get =2* total_bytes + round_up (heap_size >>4);//Try to make use of the left-over piece.        if(Bytes_left >0) {//Assign the remaining fraction to the appropriate free listobj * __volatile * my_free_list = free_list + freelist_index (bytes_left);            ((obj *) start_free), Free_list_link = *my_free_list;        *my_free_list = (obj *) start_free; } Start_free = (Char*) malloc (bytes_to_get);//apply for space to the heap        if(0= = Start_free) {//Ah, failed again.            intI obj * __volatile * my_free_list, *p;//Try to make does with what we have. That can ' t            //hurt. We don't try smaller requests, since that tends            //To result of disaster on multi-process machines.             for(i = size; I <= __max_bytes; i + = __align) {//Borrow from other brothers free listMy_free_list = free_list + freelist_index (i); p = *my_free_list;if(0! = p) {*my_free_list = P-Free_list_link; Start_free = (Char*) p; End_free = Start_free + i;return(Chunk_alloc (size, nobjs));//Recursive call, so hard not to waste                    //Any leftover piece would eventually make it to the right free list.}} End_free =0;//In case of exception. Oh, no, not yet. Only the first level configurator can be called .Start_free = (Char*) Malloc_alloc::allocate (bytes_to_get);//This should either throw an            //exception or remedy the situation. Thus we assume it            //succeeded.} heap_size + = Bytes_to_get; End_free = Start_free + bytes_to_get;return(Chunk_alloc (size, nobjs)); }}/ * Returns An object of size n, and optionally adds to size n free list.*// * We assume that n is properly aligned. *// * We hold the allocation lock. */Template <BOOLThreadsintInst>void* __default_alloc_template<threads, Inst>::refill (size_t N) {intNOBJS = -;Char* Chunk = Chunk_alloc (n, NOBJS);//default allocation of 20 N-byte spaceobj * __volatile * my_free_list;    obj * result; obj * current_obj, * next_obj;intIif(1= = NOBJS)return(chunk); My_free_list = free_list + freelist_index (n);/ * Build free list in chunk * /result = (obj *) chunk; *my_free_list = Next_obj = (obj *) (chunk + N);//The first piece has been used.       for(i =1; ;        i++) {current_obj = Next_obj; Next_obj = (obj *) (Char*) Next_obj + N);if(Nobjs-1= = i) {current_obj-free_list_link =0; Break; }Else{current_obj-free_list_link = next_obj; }      }return(result);}

STL Source code profiling (1): Space Configurator

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.