项目需求,因为需要频繁的申请和释放内存,需要更高的内存分配和释放效率,所以需要写一个效率高的内存池,因为当前项目需要的内存的大小就只有两种长度,都是小于128字节,所以用数组来做内存块的管理。
另外要考虑多线程的互斥。
内存池数据结构维护了allocated和idle两个链表,用来表示一个分配出去的,一个是空闲的。每个内存块有一个前向和后向指针
最后要和系统函数(malloc、free)进行效率比较。
操作系统:Ubuntu6.04;编程语言:c语言。
os_mutex.h 代码
#ifndef MY_OS_MUTEX_H
#define MY_OS_MUTEX_H 1
#define LINUX_OS
#if defined (LINUX_OS)
#include
#else
#endif
typedef struct osMutexStruct {
#if defined (LINUX_OS)
pthread_mutexattr_t mutexAttr;
pthread_mutex_t mutexSem; // 滑动窗口的互斥信号量;
#else
#error "[ERROR]: Need to Complete the 'OS_MUTEX_S' struct for NOT-LINUX!"
#endif
} OS_MUTEX_S;
int MutexInit(OS_MUTEX_S *semPt);
int MutexDestroy(OS_MUTEX_S *semPt);
int MutexLock(OS_MUTEX_S *semPt);
int MutexTryLock(OS_MUTEX_S *semPt);
int MutexUnLock(OS_MUTEX_S *semPt);
#endif
os_mutex.c 代码
#include
#include "os_mutex.h"
int MutexInit(OS_MUTEX_S *semPt)
{
#if defined (LINUX_OS)
pthread_mutexattr_init(&(semPt->mutexAttr));
pthread_mutexattr_settype(&(semPt->mutexAttr), PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&(semPt->mutexSem), &(semPt->mutexAttr));
return 0;
#else
#error "[ERROR]: Need to Complete the 'MutexInit' Function for NOT-LINUX!"
#endif
}
int MutexDestroy(OS_MUTEX_S *semPt)
{
#if defined (LINUX_OS)
pthread_mutex_destroy(&(semPt->mutexSem));
pthread_mutexattr_destroy(&(semPt->mutexAttr));
return 0;
#else
#error "[ERROR]: Need to Complete the 'MutexDestroy' Function for NOT-LINUX!"
#endif
}
int MutexLock(OS_MUTEX_S *semPt)
{
#if defined (LINUX_OS)
return pthread_mutex_lock(&semPt->mutexSem);
#else
#error "[ERROR]: Need to Complete the 'MutexLock' Function for NOT-LINUX!"
#endif
}
int MutexTryLock(OS_MUTEX_S *semPt)
{
#if defined (LINUX_OS)
return pthread_mutex_trylock(&semPt->mutexSem);
#else
#error "[ERROR]: Need to Complete the 'MutexLock' Function for NOT-LINUX!"
#endif
}
int MutexUnLock(OS_MUTEX_S *semPt)
{
#if defined (LINUX_OS)
return pthread_mutex_unlock(&semPt->mutexSem);
#else
#error "[ERROR]: Need to Complete the 'MutexUnLock' Function for NOT-LINUX!"
#endif
}
memory_pool.h 代码
#ifndef _MEMORY_POOL_H_
#define _MEMORY_POOL_H_
#define MP_ALIGNMENT 32 // 内存对齐字节数,按照32个字节进行字节对齐
// 内存块结构
struct mp_node_s {
struct mp_node_s *prev; // 前向指针
struct mp_node_s *next; // 后向指针
char data[128];
};
// 内存池结构
struct mp_pool_s {
struct mp_node_s *allocated; // 已分配的块
struct mp_node_s *idle; // 空闲的块
};
struct mp_pool_s *mp_create_pool(size_t size);
struct mp_node_s *mp_alloc(struct mp_pool_s *pool);
void mp_free(struct mp_pool_s *pool, struct mp_node_s *node);
void statistics(struct mp_pool_s *pool);
#endif
memory_pool.c 代码
#include
#include
#include
#include
#include
#include "memory_pool.h"
#include "os_mutex.h"
static OS_MUTEX_S memory_pool_mutex; // 内存池互斥信号量
/************************************************************************************************************************
* 函数功能:创建内存池
* 参数说明: size ------------ 创建内存块的个数
* 返 回 值: 创建的内存池的指针
************************************************************************************************************************/
struct mp_pool_s *mp_create_pool(size_t size)
{
struct mp_pool_s *pool;
int ret = posix_memalign((void **)&pool, MP_ALIGNMENT, sizeof(struct mp_pool_s) + sizeof(struct mp_node_s) * size);
if (ret) {
return NULL;
}
memset(pool, 0, sizeof(struct mp_pool_s) + sizeof(struct mp_node_s) * size);
pool->allocated = NULL;
struct mp_node_s * node = (struct mp_node_s *)((char *)pool + sizeof(struct mp_pool_s));
pool->idle = node;
node->prev = NULL;
node->next = NULL;
struct mp_node_s * tmp = node;
for (int i = 1; i < size; i++) {
node = (struct mp_node_s *)((char *)pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s) * i);
tmp->next = node;
node->prev = tmp;
node->next = NULL;
tmp = node;
}
// 初始化内存池互斥信号量
MutexInit(&memory_pool_mutex);
return pool;
}
/************************************************************************************************************************
* 函数功能:申请内存
* 参数说明: pool ------------ 内存池指针
* 返 回 值: 无
************************************************************************************************************************/
struct mp_node_s *mp_alloc(struct mp_pool_s *pool)
{
MutexLock(&memory_pool_mutex);
if (!pool->idle) {
printf("mem is null\n");
MutexUnLock(&memory_pool_mutex);
return NULL;
}
// 从idle链删除,删除首节点
struct mp_node_s *node = pool->idle;
pool->idle = node->next;
if (pool->idle){
pool->idle->prev = NULL;
}
// 加入allocated链,加入首节点
if (!pool->allocated) {
pool->allocated = node;
pool->allocated->next = NULL;
pool->allocated->prev = NULL;
} else {
node->next = pool->allocated;
pool->allocated->prev = node;
pool->allocated = node;
pool->allocated->prev = NULL;
}
MutexUnLock(&memory_pool_mutex);
return node;
}
/************************************************************************************************************************
* 函数功能:释放内存
* 参数说明: pool ------------ 内存池指针
* 返 回 值: 无
************************************************************************************************************************/
void mp_free(struct mp_pool_s *pool, struct mp_node_s *node)
{
MutexLock(&memory_pool_mutex);
// 从allocated链删除
// printf("mp_free begin, node = %p, node->next = %p, node->prev = %p\n", node, node->next, node->prev);
// 1,删除的节点前后都有节点
if (node->next && node->prev) {
node->next->prev = node->prev;
node->prev->next = node->next;
}
// 2,删除的节点前面有节点后面无节点
if (!node->next && node->prev) {
node->prev->next = NULL;
}
// 3,删除的节点前面无节点后面有节点
if (node->next && !node->prev) {
pool->allocated = node->next;
pool->allocated->prev = NULL;
}
// 4,删除的节点前后都没有节点
if (!node->next && !node->prev) {
pool->allocated = NULL;
}
// 加入到idle链
if (pool->idle) {
node->next = pool->idle;
pool->idle->prev = node;
pool->idle = node;
pool->idle->prev = NULL;
} else {
pool->idle = node;
pool->idle->next = NULL;
pool->idle->prev = NULL;
}
MutexUnLock(&memory_pool_mutex);
}
/************************************************************************************************************************
* 函数功能:统计idle和allocated的数量
* 参数说明: pool ------------ 内存池指针
* 返 回 值: 无
************************************************************************************************************************/
void statistics(struct mp_pool_s *pool)
{
int allocated = 0;
int idle = 0;
struct mp_node_s *node = pool->allocated;
while(node) {
printf("statistics, allocated, node = %p, node->next = %p\n", node, node->next);
node = node->next;
allocated++;
}
node = pool->idle;
while(node) {
printf("statistics, idle, node = %p, node->next = %p\n", node, node->next);
node = node->next;
idle++;
}
printf("allocated = %d, idle = %d\n", allocated, idle);
}
#include
#include
#include
#include
#include
#include "memory_pool.h"
int main(int argc, char *argv[]) {
int size = 10;
printf("size = %d\n", size);
struct mp_pool_s *pool = mp_create_pool(size);
printf("mp_create_pool over\n");
statistics(pool);
struct mp_node_s *node[10] = {NULL};
int i = 0;
for (i = 0;i < 10;i ++) {
node[i] = mp_alloc(pool);
printf("mp_alloc over\n");
}
statistics(pool);
// allocated队列的顺序应该是9、8、7、6、5、4、3、2、1、0
// 释放首节点
mp_free(pool, node[9]);
printf("mp_free node[9]\n");
statistics(pool);
// 释放尾节点
mp_free(pool, node[0]);
printf("mp_free node[0]\n");
statistics(pool);
// 释放中间节点
mp_free(pool, node[5]);
printf("mp_free node[5]\n");
statistics(pool);
// 再次释放首节点
mp_free(pool, node[8]);
printf("mp_free node[8]\n");
statistics(pool);
// 再次释放尾节点
mp_free(pool, node[1]);
printf("mp_free node[1]\n");
statistics(pool);
// 释放中间节点
mp_free(pool, node[4]);
printf("mp_free node[4]\n");
statistics(pool);
// 释放中间节点
mp_free(pool, node[6]);
printf("mp_free node[6]\n");
statistics(pool);
// 释放首节点
mp_free(pool, node[7]);
printf("mp_free node[7]\n");
statistics(pool);
// 释放尾节点
mp_free(pool, node[2]);
printf("mp_free node[2]\n");
statistics(pool);
// 最终释放前后都没有的节点
mp_free(pool, node[3]);
printf("mp_free node[3]\n");
statistics(pool);
return 0;
}
内存池申请了10个内存块,分配了10个内存块,分别对首节点、尾节点、中间节点进行释放。每一步有统计信息输出,测试了各种边界。
#include
#include
#include
#include
#include
#include
#include "memory_pool.h"
int timespec_minus(struct timespec *a, struct timespec *b)
{
return (a->tv_sec - b->tv_sec) * 1000000 + (int)((a->tv_nsec - b->tv_nsec)/1000);
}
int main(int argc, char *argv[]) {
int size = 1024;
printf("size = %d\n", size);
struct mp_pool_s *pool = mp_create_pool(size);
printf("mp_create_pool over\n");
// statistics(pool);
struct timespec before, after;
clock_gettime(CLOCK_MONOTONIC, &before);
struct mp_node_s *node = NULL;
int i = 0;
for (i = 0;i < 10000;i ++) {
node = mp_alloc(pool);
mp_free(pool, node);
}
clock_gettime(CLOCK_MONOTONIC, &after);
int delay = timespec_minus(&after, &before);
printf("malloc 10000 times : %d microsecond\n", delay);
// printf("mp_alloc over\n");
// statistics(pool);
return 0;
}
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 97 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 105 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 94 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 102 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 125 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 140 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 487 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 504 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 540 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 507 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 847 microsecond
(base) root@liyan-virtual-machine:/home/zhangmeng/test/libevent# ./memory_pool_test
size = 1024
mp_create_pool over
malloc 10000 times : 554 microsecond
下面是操作一万次的统计数据:
可以看到我编写的内存池(有互斥的情况下)比系统的效率还要低,失败,不能应用到项目中。从另外一个角度可以看到虽然网上很多人写内存池,如果加上互斥后效率能够跑赢系统调用的应该是很少的。我做的是非常简单的实现了还是跑不赢系统调用。