今天,对绝大多数程序来说,通用的内存分配方法--此处指代分配算符(Allocator:即malloc或new),已达到了理想的速度及满足了低碎片率的要求,然而,在内存分配领域,一丁点的信息都值得探讨很久,某些特定程序关于分配模式的信息,将有助于实现专门的分配算符,可显著地提高大多数高性能要求程序的性能底线。有时,当通用内存分配算符平均耗费几百个时钟周期时,一个良好的自定义内存分配算符可能只需要不到半打的周期。
这就是为什么大多数高性能、高要求的应用程序(如GCC、Apache、Microsoft SQL Server),都有着它们自己的内存分配算符。也许,把这些专门的内存分配算符归纳起来,放进一个库中,是个不错的想法,但是,你的程序可能有不同的分配模式,其需要另外的内存分配算符,那怎么办呢?
等等,还有呢,如果我们设计了一种特殊用途的内存分配算符,就可以不断发展下去,由此可从中筛选出一些,来组成一个通用目的的内存分配算符,如果此通用分配算符优于现有的通用分配算符,那么此项设计就是有效及实用的。
下面的示例使用了Emery小组的库--HeapLayers(http://heaplayers.org/),为了定义可配置的分配算符,其中使用了mixins(在C++社区中,也被称为Coplien递归模式):通过参数化的基来定义类,每一层中只定义两个成员函数,malloc和free:
在每一层的实现中,都有可能向它的基类请求内存,一般来说,一个不依赖于外界的内存分配算符,都会处在层次的顶层--直接向前请求系统的new和delete操作符、malloc和free函数。在HeapLayers的术语中,没有顶层堆,以下是示例:
为获取内存,顶层堆也能通过系统调用来实现,如Unix的sbrk或mmap。getSize函数的情况就比较特殊,不是每个人都需要它,定义它只是一个可选项。但如果定义了它,你所需做的只是插入一个存储内存块大小的层,并提供getSize函数,见例1:
这就是为什么大多数高性能、高要求的应用程序(如GCC、Apache、Microsoft SQL Server),都有着它们自己的内存分配算符。也许,把这些专门的内存分配算符归纳起来,放进一个库中,是个不错的想法,但是,你的程序可能有不同的分配模式,其需要另外的内存分配算符,那怎么办呢?
等等,还有呢,如果我们设计了一种特殊用途的内存分配算符,就可以不断发展下去,由此可从中筛选出一些,来组成一个通用目的的内存分配算符,如果此通用分配算符优于现有的通用分配算符,那么此项设计就是有效及实用的。
下面的示例使用了Emery小组的库--HeapLayers(http://heaplayers.org/),为了定义可配置的分配算符,其中使用了mixins(在C++社区中,也被称为Coplien递归模式):通过参数化的基来定义类,每一层中只定义两个成员函数,malloc和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); }; |
在每一层的实现中,都有可能向它的基类请求内存,一般来说,一个不依赖于外界的内存分配算符,都会处在层次的顶层--直接向前请求系统的new和delete操作符、malloc和free函数。在HeapLayers的术语中,没有顶层堆,以下是示例:
| struct MallocHeap { void * malloc(size_t sz) { return std::malloc(sz); } void free(void* p) { return std::free(p); } }; |
为获取内存,顶层堆也能通过系统调用来实现,如Unix的sbrk或mmap。getSize函数的情况就比较特殊,不是每个人都需要它,定义它只是一个可选项。但如果定义了它,你所需做的只是插入一个存储内存块大小的层,并提供getSize函数,见例1:
0
