The way the application allocates memory has a profound impact on the execution performance of the program. At present, the common memory allocation method is very efficient in nature, but there is still room for improvement.
Memory allocation, not one layer unchanged
Today, for most programs, the universal method of allocating memory--where the assignment operator (allocator: malloc or New) has reached the ideal speed and meets the low fragmentation rate requirements, however, in the memory allocation field, a little bit of information is worth exploring for a long time, The information that some specific programs have about the allocation pattern will help implement a dedicated assignment operator that can significantly improve the performance bottom line of most high-performance requirements programs. Sometimes, when a universal memory allocation operator consumes an average of hundreds of clock cycles, a good custom memory allocation operator may require less than half a dozen cycles.
This is why most high-performance, demanding applications such as GCC, Apache, and Microsoft SQL Server have their own memory allocation operators. Perhaps, it is a good idea to sum up these special memory allocation operators and put them into a library, but your program may have different allocation modes, which requires additional memory allocation operators.
And so on, and if we've designed a special-purpose memory allocation operator, can be continuously developed, from which you can filter out some of the memory allocation operators to form a common purpose, if this universal assignment operator is superior to the existing general allocation operator, then this design is effective and practical.
The following example uses the Emery Group's Library--heaplayers (http://heaplayers.org/) to define a configurable assignment operator that uses mixins (in the C + + community, Also known as Coplien recursive mode): Defines a class by a parameterized base, with only two member functions defined in each layer, malloc and free:
template <class T>
struct Allocator : public T {
void * malloc(size_t sz);
void free(void* p);
//系统相关的值
enum { Alignment = sizeof(double) };
//可选接口e
size_t getSize(const void* p);
};
In each layer of implementation, it is possible to request memory to its base class, in general, a memory allocation operator that is not dependent on the outside world is at the top of the hierarchy--directly forward to the system's new and delete operators, malloc, and free functions. In the Heaplayers terminology, there is no top-level heap, and the following is an example:
struct MallocHeap {
void * malloc(size_t sz) {
return std::malloc(sz);
}
void free(void* p) {
return std::free(p);
}
};
To get memory, the top-level heap can also be implemented through system calls, such as Unix sbrk or mmap. The case of the GetSize function is special, not everyone needs it, and it's just an option to define it. But if you define it, all you need to do is insert a layer that stores the size of the memory block and provide the GetSize function, see Example 1:
Example 1:
template <class SuperHeap>
class SizeHeap {
union freeObject {
size_t sz;
double _dummy; //对齐所需
};
public:
void * malloc(const size_t sz) {
//添加必要的空间
freeObject * ptr = (freeObject *)SuperHeap::malloc(sz + sizeof(freeObject));
//存储请求的大小
ptr->sz = sz;
return ptr + 1;
}
void free(void * ptr) {
SuperHeap::free((freeObject *) ptr - 1);
}
static size_t getSize (const void * ptr) {
return ((freeObject *)ptr - 1)->sz;
}
};
Sizeheap is how to implement a practical layer and hook it up to the best example of the malloc and free functions of its base class, which returns the modified results to the user after doing some extra work. The Sizeheap allocates additional memory for the memory block size, plus appropriate careful tuning (the union) to avoid the alignment of memory data as much as possible. It's not hard to imagine that we can build a debug heap that fills some bytes before or after a block of memory through a specific pattern, checking to see if the pattern is preserved to confirm the memory overflow. In fact, this is exactly what Heaplayers's debugheap layer does, and it's very concise.
Let's take a look again, the above is not the ideal state, some systems already provide the calculation of the allocated memory block size of the primitive (here refers to the operator, that is, the aforementioned allocation operator), on these systems, sizeheap actually only waste space. In this case, such as Microsoft Visual C + +, you will not need to sizeheap with Mallocheap because Mallcoheap will implement GetSize:
struct MallocHeap {
... 与上相同 ...
size_t getSize(void* p) {
return _msize(p);
}
};
But there seem to be some deficiencies. Think about it, we are in the statistical clock cycle, if a system of malloc declared that the memory block size will be stored in the actual block before the word, what would that be? In this case, sizeheap still wastes space because it will still store a word after the block that the system has already implanted. What is needed here is just a sizeheap method to implement the GetSize layer, but not hook malloc with free. That's why heaplayers the front sizeheap into two, see Example 2:
Example 2:
template <class Super>
struct UseSizeHeap : public Super {
static size_t getSize(const void * ptr) {
return ((freeObject *) ptr - 1)->sz;
}
protected:
union freeObject {
size_t sz;
double _dummy; //对齐所需
};
};
template <class SuperHeap>
class SizeHeap: public UseSizeHeap<SuperHeap>{
typedef typename
UseSizeHeap<SuperHeap>::freeObject
freeObject;
public:
void * malloc(const size_t sz) {
//添加必要的空间
freeObject * ptr = (freeObject *)SuperHeap::malloc(sz + sizeof(freeObject));
//存储请求的大小
ptr->sz = sz;
return (void *) (ptr + 1);
}
void free(void * ptr) {
SuperHeap::free((freeObject *)ptr - 1);
}
};
Now, Sizeheap will add the USESIZEHEAP layer correctly and use its getsize to implement it, and usesizeheap can also be used by other configurations-a very elegant design.