构建内存管理器
简介: 代码的性能优化是一项非常重要的工作。经常可以看到,采用 C 或 C++ 编写的、功能正确的软件在执行时耗费大量的内存、时间、或者在最糟的情况下既耗内存又费时间。作为一名开发人员,可以使用 C/C++ 提供的功能强大的工具来改进处理时间,并且防止内存破坏,这些工具其中之一是控制如何在代码中分配或者释放内存。
忆一下 C/C++ 中内存管理的基础知识。执行时,malloc 和 new 将向操作系统内核请求内存,而 free 和 delete 则请求释放内存。这意味着,操作系统必须在每次提出内存请求时在用户空间代码和内核代码之间进行切换。反复调用 malloc 或者 new 的程序,最终将由于不断地进行上下文切换而导致运行缓慢。
设计目标
您的内存管理器应该满足下面的设计目标:
- 速度
- 健壮性
- 用户使用便利性
- 可移植性
速度
与编译器提供的分配器相比,内存管理器的速度必须更快一些。重复的分配和释放不应该降低代码的执行速度。如果可能的话,应该对内存管理器进行优化,以便处理代码中频繁出现的某些分配模式。
健壮性
在程序终止之前,内存管理器必须归还它向系统请求的所有内存。也就是说,不应该存在内存泄漏。它还应该能够处理错误的情况(例如,请求过大的内存),并且很好地解决这些问题。
用户使用便利性
在将内存管理器集成到他们的代码中时,用户应该只需要更改很少的代码。
可移植性
应该可以很容易地将内存管理器移植到其它的系统,并且不应该使用与平台相关的内存管理特性。
创建内存管理器的实用策略
在创建内存管理器时,下面的这些策略是很实用的:
- 请求较大的内存块。
- 对常见的请求大小进行优化。
- 在容器中收集删除的内存。
请求较大的内存块
最常见的内存管理策略之一是,在程序启动期间请求一些较大的内存块,然后在代码执行期间反复地使用它们。可以从这些内存块划出部分内存,以满足各种数据结构的内存分配请求。这将极大地减少系统调用的次数,并提高执行性能。
对常见的请求大小进行优化
在任何程序中,某些特定的请求大小将比其它大小的请求更加常见。如果对您的内存管理器进行优化以便更好地处理这些请求,那么它将工作得更加出色。
在容器中收集删除的内存
应该将程序执行期间删除的内存收集到容器中。然后,应该使用这些容器来满足进一步的内存请求。如果某个请求失败,那么应该将内存访问委托给程序启动期间分配的某个较大的内存块。虽然内存管理最初用于加速程序的执行和防止内存泄漏,但这种技术可能会潜在地导致程序的较低内存空间占用,这是因为它可以重用删除的内存。这是编写您自己的内存分配器的另一个原因!
代码实现
#include <iostream> #include <time.h> #define POOLSIZE 32 //#define MY_MAMORYMANAGE //注释此行为关闭内存管理器,否则打开 using namespace std; #ifdef MY_MAMORYMANAGE class IMemoryManager { public: virtual void* allocate(size_t) = 0; virtual void free(void*) = 0; }; class Complex { public: Complex (double a, double b): r (a), c (b) {} inline void* operator new(size_t); inline void operator delete(void*);private: double r; // Real Part double c; // Complex Part }; class MemoryManager: public IMemoryManager { public: MemoryManager () { freeStoreHead = 0; expandPoolSize (); } virtual ~MemoryManager () { cleanUp (); } virtual void* allocate(size_t); virtual void free(void*); private: struct FreeStore { FreeStore *next; }; void expandPoolSize (); void cleanUp (); FreeStore* freeStoreHead; }; MemoryManager gMemoryManager; // Memory Manager, global variable inline void* MemoryManager::allocate(size_t size) { if (0 == freeStoreHead) expandPoolSize (); FreeStore* head = freeStoreHead; freeStoreHead = head->next; return head; } inline void MemoryManager::free(void* deleted) { FreeStore* head = static_cast <FreeStore*> (deleted); head->next = freeStoreHead; freeStoreHead = head; } void MemoryManager::expandPoolSize () { size_t size = (sizeof(Complex) > sizeof(FreeStore*)) ? sizeof(Complex) : sizeof(FreeStore*); FreeStore* head = reinterpret_cast <FreeStore*> (new char[size]); freeStoreHead = head; for (int i = 0; i < POOLSIZE; i++) { head->next = reinterpret_cast <FreeStore*> (new char [size]); head = head->next; } head->next = 0; } void MemoryManager::cleanUp() { FreeStore* nextPtr = freeStoreHead; for (; nextPtr; nextPtr = freeStoreHead) { freeStoreHead = freeStoreHead->next; delete [] nextPtr; // remember this was a char array } } void* Complex::operator new (size_t size) { return gMemoryManager.allocate(size); } void Complex::operator delete (void* pointerToDelete) { gMemoryManager.free(pointerToDelete); } #else class Complex { public: Complex (double a, double b): r (a), c (b) {} private: double r; // Real Part double c; // Complex Part }; #endif // MY_MAMORYMANAGE int main(int argc, char* argv[]) { Complex* array[1000]; clock_t start_t,end_t; start_t = clock(); for (int i = 0;i < 5000; i++) { for (int j = 0; j < 1000; j++) { array[j] = new Complex (i, j); } for (j = 0; j < 1000; j++) { delete array[j]; } } end_t = clock(); cout<<(end_t-start_t)/1000.0<<endl; return 0; }
实验结果(windows下VC6.0):
未使用自构建的内存管理器,平均耗时:1.9秒+
使用后平均:0.4秒+
P.S.开源项目MYSQL中也是使用自己构建的内存管理器,以加快程序速度和保证程序健壮性。以上文字均来源于网络,这里只是重排而已。