c语言实现简单的内存池

目的

        项目需求,因为需要频繁的申请和释放内存,需要更高的内存分配和释放效率,所以需要写一个效率高的内存池,因为当前项目需要的内存的大小就只有两种长度,都是小于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

性能测试结论

       下面是操作一万次的统计数据:

  1. 系统内存分配释放的时间,不包括memset,平均时间大概是: 440 微秒
  2. 用我写的内存池分配释放的时间,没有互斥,不包括memset,平均时间大概是:110 微秒
  3. 用我写的内存池分配释放的时间,有互斥,不包括memset,平均时间大概是:573 微秒

可以看到我编写的内存池(有互斥的情况下)比系统的效率还要低,失败,不能应用到项目中。从另外一个角度可以看到虽然网上很多人写内存池,如果加上互斥后效率能够跑赢系统调用的应该是很少的。我做的是非常简单的实现了还是跑不赢系统调用。

你可能感兴趣的:(c语言,服务器)