STL space configurator.
STL Introduction
STL (Standard Template Library, Standard Template Library), fundamentally speaking, STL is a set of "containers" which include list, vector, set, map, etc, STL is also a set of algorithms and other components.
When talking about components, let's first briefly discuss the six STL components, their use of relevant design patterns, and the collaboration between components.
Design Patterns
Six Components
1. space configurator: memory pool for small memory allocation, corresponding to the design mode-singleton mode (Tool class, providing services, a program only needs one space configurator ), share mode (small memory is managed by the memory pool)
2. iterator: iterator mode, template method
3. container: one of the core of STL. Other components work around the container: The iterator provides the access mode, the space configurator provides the container memory allocation, and the algorithm processes the data in the container, the pseudo-algorithm of the imitation function provides specific policies for extracting the internal types of the custom types through type extraction. Guarantees algorithm coverage. The design modes involved include the combination mode (tree structure), the facade mode (provided by external interfaces), and the adapter mode (stack and queue are adapted through deque ), builder mode (creation process of different types of trees ).
4. Type extraction: Internal type parsing Based on Model programming, obtained through typename. You can obtain the internal types of the iterator, such as value_type, Poter, and Reference.
5. Function imitation: a callback mechanism similar to a function pointer for Decision Processing in algorithms. Involved: Policy mode and template method.
6 adapter: the stack in STL. The queue is implemented through the deque adaptation of the dual-end queue, and the map and set are implemented through the RB-Tree adaptation. The adapter mode is involved.
A brief description of the specific relationship between the six components
Ps (image technology is relatively watery. Sorry, please correct me if you have any bugs)
It seems a lot more involved. Let's talk about the topic space configurator.
The reason for the STL space configurator:
In software development and program design, we can't help but use a lot of small memory (Basic Types and custom types of small memory) due to program requirements ). Dynamically apply for and release in the program.
This process is not always well controlled,
Problem 1: memory fragmentation occurs. (External fragment problem)
Problem 2: I have been applying for memory because of small memory blocks. Calling malloc causes performance problems in system calls.
Note: internal fragmentation occurs because of memory alignment and access efficiency (number of CPU addresses retrieved). If the user needs 3 bytes, the problem is actually 4 or 8 bytes, fragments are wasted.
External fragmentation: The amount of memory in the system is sufficient, but not consecutive. Therefore, it is a waste of resources that cannot be allocated to users. Simple illustration below
After explaining these two questions, let's talk about the implementation details of the STL space configurator.
Implementation Policy
Is the user's application space larger than 128?
Yes: Call the level-1 space Configurator
No: Call the second-level space Configurator
The general implementation is as follows:
The second-level space configuration consists of a memory pool and a partner system: a free linked list.
The level-1 space configurator directly encapsulates malloc and free for processing, and adds the set_handler Mechanism in C ++ (this is actually a slightly far-fetched decoration/adaptation mode ), the client can choose the processing mechanism when memory allocation is added.
Configurability:
The client can use macro _ USE_MALLOC to customize whether to use the second-level space configurator.
The level-1 space configurator mainly encapsulates malloc and adds the handler mechanism, so it won't be too long here. I believe you can understand it through the source code.
About the second-level space configurator:
Finally, let's talk about the Trace problem and then give the code.
Trace usage
The internal implementation process of the memory pool is still relatively complicated, although the amount of code, the function is relatively simple. However, the call process may be complicated. At this time, if we select debug debugging, the process will be quite complicated. We need to carefully record the call stack process, data flow direction, and logical changes. It is estimated that it will be hard for the landlord to complete the process.
Therefore, Trace is used for tracking,Print the data flow direction, logic direction, file, function, method, and row location.Then we can troubleshoot and optimize the program based on this record.
The Trace is as follows:
#pragma once#define ___TRACE(...) fprintf(fout, "file[%s]line[%u]func[%s]::",__FILE__,__LINE__,__func__);\fprintf(fout,__VA_ARGS__)
That's right, it's that simple. You can use a macro to print files, rows, and function locations, and then use a variable parameter list to receive records of specific locations in the code.
The following is the link in Alloc extracted from the code.
static void *Allocate(size_t n) { ___TRACE("__MallocAllocTemplate to get n = %u\n",n); void *result = malloc(n); if (0 == result) { result = OomMalloc(n); } return result; }
My day: the Organization is not good, the hand speed is too bad, and finally the prelude is complete. The code of the Space Configurator is provided.
You can also obtain the source code in your github.
Https://github.com/langya0/llhProjectFile/tree/master/STL
File: Alloc. h
# Pragma once # include "Config. h "_ STLBEGIN # include <memory. h> # include <stdlib. h> # define _ THROW_BAD_ALLOC throw std: bad_alloc () class _ MallocAllocTemplate {private: static void * OomMalloc (size_t); static void * OomRealloc (void *, size_t ); static void (* _ malloc_alloc_oom_handler) (); public: static void * Allocate (size_t n) {___ TRACE ("_ MallocAllocTemplate to get n = % u \ n ", n); void * result = malloc (n ); If (0 = result) {result = OomMalloc (n);} return result;} static void * Reallocate (void * p, size_t old_sz, size_t new_sz) {void * result = realloc (p, new_sz); if (0 = result) {result = OomRealloc (p, new_sz); return result ;}} static void Deallocate (void * p, size_t n) {___ TRACE ("first-level space configurator releases p = % p n = % u \ n", p, n ); free (p);} // static void (* set_malloc_handler (void (* f) () {___ TRACE ("Level 1 space configurator, set Handler f = % p \ n ", f); void (* old) () = _ malloc_alloc_oom_handler; _ malloc_alloc_oom_handler = f; return (old );}}; void * _ MallocAllocTemplate: OomMalloc (size_t n) {___ TRACE ("Level 1 space configurator, less than entering Oo n = % u \ n", n ); void (* my_malloc_handler) (); void * result; for (;) {my_malloc_handler = _ handler; if (0 = my_malloc_handler) {_ THROW_BAD_ALLOC ;} (* _ malloc_alloc_oom_handler) (); result = ma Lloc (n); if (result) return result ;}} void * _ MallocAllocTemplate: OomRealloc (void * p, 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 = realloc (p, n); if (result) return result ;}} void (* _ MallocAllocTemplate: :__ malloc_alloc_oom_handler) () = 0; typedef __ MallocAllocTemplate MallocAlloc; ///// put it here # the front edge of ifdef is because the level-1 space configurator still needs to be called in the level-2 space configurator //////////////// /// // determines whether to configure _ USE_ALLOC to use the level-1 or level-2 space configurator # ifdef _ USE_ALLOCtypedef MallocAlloc Alloc; # else // not define _ USE_ALLOCtemplate <bool threads, int inst> class _ DefaultAllocTemplate {protected: enum {_ ALIGN = 8}; enum {_ MAX_BYTES = 128 }; enum {_ NFREELISTS = 16}; // _ MAX_BYTES/_ ALIGN static size_t R OundUp (size_t bytes) {return (bytes) + (size_t) _ ALIGN-1 )&~ (Size_t) _ ALIGN-1);} protected: union _ Obj {// node link pointer union _ Obj * _ freeListLink; // client data char _ ClientData [1] ;}; // bucket structure, save the linked list static _ Obj * _ freeList [_ NFREELISTS]; // obtain the subscript of a specific size element in the table static size_t _ FreeListIndex (size_t _ bytes) {return (_ bytes) + (size_t) _ ALIGN-1) /(size_t) _ ALIGN-1);} static char * _ start_free; static char * _ end_free; static size_t _ heap_size; public: static void * Allocate (size _ T n) {void * ret = 0; ___ TRACE ("second-level space configurator applies for n = % u \ n", n); if (n> _ MAX_BYTES) ret = MallocAlloc: Allocate (n); _ Obj * volatile * _ my_free_list = _ freeList + _ FreeListIndex (n); _ Obj * _ result = * _ my_free_list; if (_ result = 0) ret = _ Refill (RoundUp (n); else {* _ my_free_list = _ result-> _ freeListLink; ret = _ result;} return ret;} static void Deallocate (void * p, size_t n) {___ TRACE ("second-level space configurator deletes p = % P, n = % d \ n ", p, n); if (n> (size_t) _ MAX_BYTES) MallocAlloc: Deallocate (p, n ); else {_ Obj * volatile * _ my_free_list = _ freeList + _ FreeListIndex (n); _ Obj * q = (_ Obj *) p; q-> _ freeListLink = * _ my_free_list; * _ my_free_list = q;} static void * Reallocate (void * p, size_t _ old_sz, size_t _ new_sz) {___ TRACE ("second-level space configurator re-applies p = % p, new_sz = % d \ n", p ,__ new_sz); void * _ result; size_t _ copy_sz; if (_ ol D_sz> (size_t) _ MAX_BYTES & _ new_sz> (size_t) _ MAX_BYTES) {return (realloc (p, _ new_sz ));} if (RoundUp (_ old_sz) = RoundUp (_ new_sz) return (p); _ result = Allocate (_ new_sz ); _ copy_sz = _ new_sz> _ old_sz? _ Old_sz: _ new_sz; memcpy (_ result, p, _ copy_sz); Deallocate (p, _ old_sz); return (_ result);} protected: static void * _ Refill (size_t n) {___ TRACE ("list space configurator free linked list filling n = % u \ n", n); size_t nobjs = 20; void * ret; char * chunks = _ ChunkAlloc (n, nobjs); if (nobjs = 1) {return chunks ;} _ Obj * volatile * _ my_free_list = _ freeList + _ FreeListIndex (n); ret = chunks; for (size_t I = 1; I <nobjs; I ++) {(_ Obj *)( Chunks + n * I)-> _ freeListLink = * _ my_free_list; * _ my_free_list = (_ Obj *) (chunks + n * I);} return ret ;} /////////// nobjs Usage & here, the logic needs to be reused internally and the static char * _ ChunkAlloc (size_t n, size_t & nobjs) {size_t totalBytes = n * nobjs; size_t bytesLeft = _ end_free-_ start_free; if (bytesLeft> = totalBytes) {___ TRACE ("second-level space configurator memory pool fill n = % u, nobjs = % d \ n", n, nobjs); _ start_free + = n * nobjs; return _ start_free;} else If (bytesLeft> = n) {nobjs = (_ end_free-_ start_free)/n; ___ TRACE ("second-level space configurator memory pool fill n = % u, nobjs = % d \ n ", n, nobjs); _ start_free + = n * nobjs; return _ start_free;} // bytesLeft [0, 1) _ Obj * volatile * _ my_free_list = _ freeList + _ FreeListIndex (bytesLeft); if (_ start_free) {___ TRACE ("remaining bytesLeft = % u \ n", bytesLeft); (_ Obj *) _ start_free)-> _ freeListLink = * _ my_free_list; * _ my_free_list = (_ Obj *) _ start_free ;} Size_t bytesToGet = nobjs * n * 2 + (_ heap_size> 4); // malloc _ start_free = (char *) malloc (bytesToGet); if (! _ Start_free) {___ TRACE ("malloc, second-level space configurator failed to search for n = % d \ n", n); for (size_t I = n + _ ALIGN; I <_ MAX_BYTES; I + = _ ALIGN) {_ Obj * volatile * cur = _ freeList + _ FreeListIndex (I); if (* cur) {* cur = (* cur)-> _ freeListLink; _ start_free = (char *) * cur; _ end_free = _ start_free + I; return _ ChunkAlloc (n, nobjs) ;}___ TRACE ("second-level space configurator: Subsequent linked list query failed, transfer the first-level configuration, use the handler mechanism to terminate the program or get the space n = % d \ n ", n); _ start_free = (char *) MallocAlloc :: Allocate (n); return _ ChunkAlloc (n, nobjs);} else {_ end_free = _ start_free + bytesToGet; _ heap_size + = bytesToGet; return _ ChunkAlloc (n, nobjs) ;}}; template <bool _ threads, int _ inst> char * _ DefaultAllocTemplate <__threads, _ inst >:: _ start_free = 0; template <bool _ threads, int _ inst> char * _ DefaultAllocTemplate <__threads, _ inst >:: _ end_free = 0; template <bool _ threads, int _ inst> size_t _ Defa UltAllocTemplate <__ threads, _ inst >:: _ heap_size = 0; // static _ Obj * _ freeList [_ NFREELISTS]; template <bool threads, int inst> typename _ DefaultAllocTemplate <threads, inst >:: _ Obj * _ DefaultAllocTemplate <threads, inst >:: _ freeList [_ DefaultAllocTemplate <threads, inst> :: _ NFREELISTS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; typedef _ DefaultAllocTemplate <0, 0> Alloc; # endif _ STLENDvoid handler (){ Cout <"here in handler! \ N "<endl;} void test () {// stl: Alloc: set_malloc_handler (handler); void * arr [21] = {0 }; ___ TRACE ("Clint to Get size = % u \ n", 5); // for (size_t I = 0; I <21; ++ I) // arr [I] = stl: Alloc: Allocate (5); // for (size_t I = 0; I <21; ++ I) // stl :: alloc: Deallocate (arr [I], 5); arr [1] = stl: Alloc: Allocate (8); arr [2] = stl: Alloc :: allocate (72); arr [3] = stl: Alloc: Allocate (80 );}View Code
File: Trace. h
#pragma once#define ___TRACE(...) fprintf(fout, "file[%s]line[%u]func[%s]::",__FILE__,__LINE__,__func__);\fprintf(fout,__VA_ARGS__)
File Config. h
# Pragma once //// simulate the macro-style Implementation of std, end namespace _ STLNAMESPACE {}# define _ STLBEGIN namespace _ STLNAMESPACE {# define _ STLEND} namespace stl = _ STLNAMESPACE; // for malloc // # define _ USE_MALLOC // for trace # define _ DEBUG # ifdef _ DEBUG # include <stdio. h> # define fout stdout // easy to configure and modify the Trace record position # endif
The finished source code is finished, and the test result is given again.
Finally, after completing the theme work.
Let's talk about some problems left by the Space configurator: 1. After carefully exploring the source code, you will surely find a problem,
It seems that the second-level space configurator does not return the space to the system from the beginning to the end. The question is, when will the memory pool space be released?
For this problem, go back to the source code and structure, and you will find
If the memory is greater than 128, the customer program Deallocate will call free to release it and return it to the system.
But ...............
The space obtained from the memory pool is eventually assumed that the user has called Dealloc to release the call, so where are they?
Not returned to the system, not in the memory pool, and in the free linked list.
Got it: the program has never been released, but is in the free linked list. All the methods and members of the configurator are static, so they are stored in the static zone. The release time is the end of the program.
2. What should I do if I need to release it?
Because only unused values in the free linked list can be returned when the program runs, but they are not necessarily continuous (the user applies for space and the uncontrollable order of Space release ), therefore, if you want to release it at the appropriate time (for example, release it in the handler of the first-level configurator, or set the threshold values and handle the allocated space when it reaches), you must ensure that the released space is continuous. The continuous solution is to track the distribution and release process and record the node confidence. Only contiguous blocks are released.
3. Excellent efficiency of STL space Configurator
Since it already exists and is widely used, the overall efficiency and the use of the STL internal container are still correct.
We consider the following situations:
A. the user only needs an infinite char space, but alignment in the Configurator is 8. Thus, there will be 7/8 of the space waste in the whole program.
B. Assume that the user applies for 8 spaces N times, the system resources are consumed to a certain extent, and all the space in the free linked list is continuously released. But not released.
However, when you need to apply for a space larger than 8, there is still no space available.
In short: the problem is that all the space may be stored in a small free linked list, but there is no user available. This is embarrassing.
Finally, if you have any questions about the configurator, please contact us. Email: 15829391774@163.com