Implementation of a memory pool

Source: Internet
Author: User

Implementation of a memory pool

The last two days have studied the memory pool in SGI STL, there are a lot of explanations for this piece, but it's either incomplete or not simple enough (at least for beginners like me ...), so I'm going to write down my understanding of SGI STL. Easy to review at the same time also hope to be like me just contact C + + beginner to provide some help.

First we need to be clear, what is the purpose of the memory pool? The first thing you need to know is that every time we use new T to initialize the type T, there are two steps, one called memory allocation, which is actually not new but operator new (also known as malloc in C), a step that deals directly with the operating system. , the operating system may need a relatively tedious process to return a pointer to the free memory to the user, so this is also a time-consuming part of new, and the second step is to use the constructor to initialize the memory, which we are more familiar with. Since memory allocation is time-consuming, it's easy to think of allocating a chunk of memory at once and then dividing it to the user when the user needs it, so that one allocation, multiple uses, naturally improves efficiency, and is used to manage this so-called chunk of memory data structure, That's the memory pool we're talking about today. Another benefit is that the frequent use of new will result in a serious fragmentation of the system's memory space, with the result that it is difficult to find a contiguous chunk of memory with low space utilization.

So let's take a look at the overall structure of the memory pool:

The memory pool can be thought of by an array of pointers above and the following two parts of the free list, the pointer array of the first pointer to the memory size of 8bytes nodes connected to a free list, followed by memory and 16bytes, 24bytes until 128bytes, Of course I only drew a free list in the picture. So the basic idea of a memory pool is:

1. If the user allocates more than 128bytes of memory, directly with malloc, otherwise find the appropriate free-link list, from which a node is picked up to return its head pointer to the user.

2. The release process is exactly the same as the allocation, if the user allocates more than 128bytes of memory, directly with free, otherwise find the appropriate Freedom list, the pointer refers to the memory reconnect to the free list (note that this is not returned to the operating system, because it can be reused later).

The corresponding code for this section is as follows:

1 private:2     static const int Align = 8; 3     static const int maxbytes = 4,     static const int Numberoffreelis ts = maxbytes/align; 5     static const int numberofaddednodesforeachtime = 6  7     Union node {8         Union node *next; 9         Char CLI Ent[1];10     };11-     static obj *freelists[numberoffreelists];

For the sake of understanding, I have made the corresponding change to the attribute name in the source code, the only possible question is why this node can use the union? Here we need to figure out that the free list is all about a node that is not in use, and we can certainly allocate space to implement this function, for example, in order to connect all nodes together, such as the more likely way to think of it, Use a struct to maintain the block of memory that is actually assigned to the user and the next struct. However, there are two disadvantages to doing so:

1. First, each node of it needs an extra pointer space to hold the address of the memory block that is really being allocated to the user.

2. Next, after allocating the memory block, it is necessary to dispose of the node's corresponding struct.

Before we analyze the code for the allocation function, let's look at a few helper functions:

1 private:2     static size_t round_up (size_t size) {3         return (size + Align-1) & ~ (Align-1)); 4     }5 6     S Tatic size_t freelist_index (size_t size) {7         return (size + Align-1)/align-1;8     }

These two functions are simple, the first one returns a multiple of 8 greater than or equal to the input value, and the second returns the smallest free-link table that can hold the input value.

Next is the memory pool external interface, allocate function implementation code.

1 void* alloc::allocate (size_t size) {2     if (Size > MaxBytes) {3         return malloc (size); 4     } 5  6     size _t index = freelist_index (size); 7     node* themostsuitablenode = Freelists[index]; 8     if (themostsuitablenode) {9         Freelists[index] = Themostsuitablenode->next;10         return themostsuitablenode;11     }12     else {         return refill (round_ Up (size));     }15}

1. As we said earlier, when the user wants to get the size of the memory space, at this point we just need to find the smallest can accommodate size of the free list, because the free list is still unallocated space, if there is a node in the free list, the node can be directly assigned to it, This is the case where the themostsuitablenode is not empty, but at this point we are going to point the pointer to the free list in the array to the next node, because this node is already assigned.

2. On the other hand, if there is no node available in the free list (there are two cases where node is not available, the first one has been allocated, but is used up, the second is that this is the first time that the memory pool has been initialized with a free list of this size, so no space has been allocated), We use the refill function directly to populate the free list, the reason is to use round_up to make it a multiple of 8, because in the efficiency we may allocate more than 1 node (here is 20), so the space here must be allocated according to the size of node.

So we Shinuna, and then we look at the implementation code of refill.

1 void* alloc::refill (size_t size) {2     size_t num = numberofaddednodesforeachtime; 3     char* block = blockalloc (size , num); 4     node** currentfreelist = 0; 5     node *curnode = 0, *nextnode = 0; 6  7     if (num = = 1) {8         return block; 9     }10     else {         currentfreelist = freelists + freelist_index (size);         *currentfreelist = NextNode = Reinterpret_cast<node*> (block + size), + for         (int i = 1;; ++i) {             Curnode = nextnode;15             NextNode = r Einterpret_cast<node*> (reinterpret_cast<char*> (curnode) + size),             if (num-1 = = i) {                 Curnode->next = 0;18                 break;19             }20             else {                 Curnode->next = nextnode;22             }23         }24         return block;25     } 26}

First explain the second line of the Blockalloc, the function is to go to the memory pool to find size * num sizes of space and then divided into the current free list (that is, currentfreelist), Because once the refill is called, there is no assignable node for the free list, so we are allocating numberofaddednodesforeachtime (that is, 20) when we consider redistribution here. But pay attention to the fact that here num passes in the reference, why is it quoted? Because there is also the possibility that there is insufficient memory pool space, if the memory pool is 1 node but not enough 20, NUM will be set to 1, indicating that only 1 node space is allocated at this time. So you can see the 26th line of judgment, when NUM is 1, the block will be directly returned to the user. If it is not 1, then the remaining nodes will be threaded to the free list before returning. This is the function of the For loop.

Before touching the blockalloc, of course, let's take a look at another familiar memory pool.

1 static char *startofpool, *endofpool;

These two variables point to the starting and ending points in the space allocated by the memory pool, before saying that if there is no node in the free list, then the memory pool is taken, in fact, it is the space that is needed from the location where the startofpool started.

The last direct contact with the memory pool is of course blockalloc, so let's take a look at this function as well.

1 char* alloc::blockalloc (size_t size, size_t& num) {2     char* re = 0; 3     size_t bytesneeded = size * num; 4
   
    size_t bytesleft = Endofpool-startofpool; 5  6     if (bytesleft >= bytesneeded) {7         re = Startofpool; 8         startofpool = Startofpool + bytesneeded; 9
    return re;10     }11     else if (Bytesleft > size) {         num = bytesleft/size;13         re = startofpool;14         Startofpool + = num * size;15         return re;16     }17     else {         //todo19}20     }
   

There are three cases, the first is that if the space is enough (enough to allocate 20 node size), it is allocated directly, then the Startofpool to the beginning of the space in the memory pool to move to a new location, the second is not enough to allocate 20, but enough to allocate one, this time using the same way , just need to change num (because NUM is a reference here, so there is no big problem), the last case is that even a node of the memory can not be taken out, this situation requires the system to request memory, I will explain in detail below. Here we first to the rationale, the current situation ...

1. Use allocate to request a size-sized memory space to the memory pool.

2. Allocate finds the most suitable free-form list based on size.

A. If the list is not empty, the first node is returned, and the list header is changed to a second node.

B. If the list is empty, use the Blockalloc request to assign node.

X. If there is more than one node in the memory pool, allocate as many node as possible (but up to 20), return one node and add the other node to the list.

Y. If the memory pool has only one node space, it is returned directly to the user.

Z. If you don't have a single node, try allocating memory to the operating system again (this is the TODO part of the code above).

Then we can also find several features of the memory pool:

1. When the memory pool is initially initialized, there is no memory in the memory pool, and all the free lists are empty lists.

2. Only when the user requests memory to the memory pool for the first time, the memory pool executes the 1->2->b->z of the above procedure in turn to complete the memory pool and the first population of the linked list, while the other unused list is still empty.

With this overall understanding, let's take a look at how the memory pool is requesting memory from the operating system:

 1 char* alloc::blockalloc (size_t size, size_t& num) {2 char* re = 0; 3 size_t bytesneeded = size * NUM; 4 size_t bytesleft = Endofpool-startofpool;  5 6 if (Bytesleft >= bytesneeded) {7 re = Startofpool; 8 Startofpool = Startofpool + bytesneeded; 9 return re;10}11 else if (Bytesleft > size) {num = bytesleft/size;13 re = start OFPOOL;14 Startofpool + = num * size;15 return re;16}17 else {//I am not sure why add Round_up (poolsize >> 4) size_t bytestoget = 2 * bytesneeded + round_up (poolsize >> 4); (Bytesleft > 0) {node** themostsuitablelist = freelists + freelist_index (bytesleft); E*> (startofpool))->next = *themostsuitablelist;23 *themostsuitablelist = reinterpret_cast<node*> (s Tartofpool),}25 Startofpool = (char*) malloc (bytestoget); 27        if (!startofpool) {node** currentfreelist = 0;29 node* Listheadnode = 0;30 for (int i = size + Align; I <= maxbytes; i + = Align) {currentfreelist = freelists + Freelist_index ( i); Listheadnode = *currentfreelist;33 if (listheadnode) {*curren                     Tfreelist = listheadnode->next;35 Startofpool = reinterpret_cast<char*> (Listheadnode); 36  Endofpool = reinterpret_cast<char*> (Listheadnode + i), PNS return blockalloc (size, num);}39}40//if code can run into this place, it means we can no longer get Any memeory, so the best-of-the-exception ... $ exit (3),}43 else {poolsize + = bytestoget;45 Endofpool = start Ofpool + bytestoget;46 return blockalloc (size, num); 47}48}49}

You will find that when the space is not enough, the first calculation of the required memory is the Bytestoget, I also mentioned in the code I do not know why to add a round_up (...), and then the current remaining memory (if there is a surplus) allocated to the appropriate node, Since each allocation of memory is a multiple of 8, so long as there is a surplus, it is certainly a multiple of 8, so it is certain to find the appropriate node. Then began to allocate memory, if the allocation of memory failure, then start from size + align (in fact, the source code seems to start from size, but I feel that the size of a node at this time the free list is obviously empty, otherwise it will not call this function, so the direct size + align Start), if you can move a node from those positions (obviously the node to be larger), then you can complete the allocation, if you traverse all the larger than the size of the node to find such a node, as I said in the code, run to that position should be thrown out of the exception. In addition, if the allocation succeeds, after updating the corresponding variable, the function is called again for allocation, and there is enough memory in the memory pool to allocate to the free list.

The whole process of memory allocation is over here, and the following is the release of memory:

1 void Alloc::d eallocate (void* ptr, size_t size) {2     if (Size > MaxBytes) {3 free         (PTR); 4     } 5     else {6         size_t index = freelist_index (size), 7         static_cast<node*> (PTR)->next = Freelists[index]; 8         Freelists[index] = static_cast<node*> (PTR); 9     }10}

The release of memory is very simple, if it is greater than 128bytes, direct release (because it is also directly assigned), otherwise it is linked to the corresponding list, left to use later.

Here, the implementation of the memory pool is complete, but before it is actually put into the actual use of the STL, a layer of encapsulation is needed.

    Public:typedef T Value_type;        typedef t* Pointer;        typedef const T* Const_pointer;        typedef t& Reference;        typedef const t& Const_reference;        typedef size_t SIZE_TYPE;    typedef ptrdiff_t DIFFERENCE_TYPE;        Public:static T *allocate ();        Static T *allocate (size_t N);        static void Deallocate (T *ptr);        static void Deallocate (T *ptr, size_t N);        static void construct (T *ptr);        static void construct (T *ptr, const t& value);        static void Destroy (T *ptr);    static void Destroy (T *first, T *last);    }; Template<class t> T *allocator<t>::allocate () {return static_cast<t *> (alloc::allocate (sizeof (    T)));        } template<class t> T *allocator<t>::allocate (size_t N) {if (n = = 0) return 0;    Return static_cast<t *> (alloc::allocate (sizeof (T) * n));  } Template<class t>  void Allocator<t>::d eallocate (t *ptr) {alloc::d eallocate (static_cast<void *> (PTR), sizeof (t));        } template<class t> void allocator<t>::d eallocate (T *ptr, size_t N) {if (n = = 0) return;    Alloc::d eallocate (static_cast<void *> (PTR), sizeof (T) * n);    } template<class t> void Allocator<t>::construct (t *ptr) {new (PTR) T ();    } template<class t> void Allocator<t>::construct (t *ptr, const t& value) {new (PTR) T (value);    } template<class t> void allocator<t>::d Estroy (T *ptr) {ptr->~t (); } template<class t> void allocator<t>::d Estroy (t *first, T *last) {for (; first! = last; ++first)        {first->~t (); }    }}

This is also the interface of allocator in our familiar standard library ...

So the idea of the final memory pool is this:

1. Use allocate to request a size memory space to the memory pool, and use malloc directly if the requested memory size is greater than 128bytes.

2. If the required memory size is less than 128bytes, allocate finds the most suitable free-form list based on size.

A. If the list is not empty, the first node is returned, and the list header is changed to a second node.

B. If the list is empty, use the Blockalloc request to assign node.

X. If there is more than one node in the memory pool, allocate as many node as possible (but up to 20), return one node and add the other node to the list.

Y. If the memory pool has only one node space, it is returned directly to the user.

Z. If you do not have a node, allocate memory again to the operating system request.

① assignment succeeds, B process again

② allocation failure, loop through each free list, find space

I. Find space and proceed to process B again

II. Unable to find space, throw an exception (not given in the code, just give a comment)

3. The user calls deallocate to free up memory space and calls free if the required memory space is greater than 128bytes.

4. If not, find the right free list by its size and insert it.

The features are actually like this:

1. When the memory pool is initially initialized, there is no memory in the memory pool, and all the free lists are empty lists.

2. Only when the user requests memory to the memory pool for the first time, the memory pool executes the 1->2->b->z of the above procedure in turn to complete the memory pool and the first population of the linked list, while the other unused list is still empty.

3. All allocated memory does not have any records in the memory pool, and the release or not is completely self-conscious by the programmer.

4. When the memory is released, if it is greater than 128bytes, it is directly free, otherwise it will be added to the corresponding Freedom list instead of being returned directly to the operating system.

The above is my understanding of SGI STL memory pool, if there is any wrong place, welcome to point out, thank you ...

Category: C + + gossip

Implementation of a memory pool

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.