customize your own memory allocator (2)

I have made a more sophisticated version of memory allocator based on mmap. It can avoid memory increacement issue happened in traditional memory allocator, e.g. glibc malloc/free. However, it has two disadvantages. One of them is that this allocator is slower than glibc malloc/free since it call mmap/munmap frequently. The other one is that it does not support multi-threads.

The source code is as following which may contain bugs, since I just did some simple tests, a random test. More test should be done before it really used to substitute glibc memory allocator.

--------------------------------------------------------------------------------

[torstan]$ more malloc.h

#ifndef _MALLOC_MY_H
#define _MALLOC_MY_H
#include<stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

void* malloc(size_t sz);
void free(void *p);

#ifdef __cplusplus
}
#endif

#endif

 

[torstan]$ more malloc.c

#include "malloc.h"
#include <sys/mman.h>

#define BINNUM 8
#define LARGEOBJ 1024
#define PAGESIZE 4096
#define PAGE_START(p) ((char*)(((size_t)(p))/PAGESIZE*PAGESIZE))
#define DBG(x) {x}
//#define DBG(x)

struct _Item{
struct _Item* next;
};
typedef struct _Item Item;

struct _Mmap_alloc_header{
void* saddr; //start address
size_t nsize; //bytes of memory requested
};
typedef struct _Mmap_alloc_header Mmap_alloc_header;

struct _Page_header{
struct _Page_header* next; //next page
size_t nsize; //size of chunk
struct _Page_header* prev; //prev page
Item* freelist; //free list in this page
size_t nfree; //number of free chunks
};
typedef struct _Page_header Page_header;

static size_t bin[BINNUM] = {8,16,32,64,128,256,512,1024};
static Page_header* full_bin[BINNUM] = {0};
static Page_header* free_bin[BINNUM] = {0};

void* remove_head(Page_header** list)
{
Page_header* head = *list; //a copy of pointer
Page_header* page = head;
if(head->next)
head->next->prev = 0;
head = head->next;
*list = head;
return page;
}

void* remove_page(Page_header** list, Page_header* page) //remove page from list
{
Page_header* head = *list;
Page_header* prev = page->prev;
Page_header* next = page->next;
if(head == page)
return remove_head(list);
if(prev)
prev->next = next;
if(next)
next->prev = prev;
page->next = page->prev = 0;
return page;
}

void add_head(Page_header** list, Page_header* page) //add page to head of list
{
Page_header* head = *list;
page->next = head;
page->prev = 0;
if(head)
head->prev = page;
head = page;
*list = head;
}

int get_binsize(size_t sz)
{
int i = 0;
if(sz > LARGEOBJ)
return -1;
if(sz <= bin[0])
return 0;
for(i=1;i<BINNUM;i++)
{
if(sz > bin[i-1] && sz<= bin[i])
return i;
}
return -1;
}

void* allocate_page(int bid)
{
char* p;
size_t i, n = bin[bid], num = (PAGESIZE - sizeof(Page_header))/n;
char* page = (char*)mmap(NULL, PAGESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(bid < 0 || bid >= BINNUM) return 0;
if(page == MAP_FAILED || !page) return 0;
Page_header* header = (Page_header*)page;
header->next = 0;
header->prev = 0;
header->nfree = num;
header->nsize = n;
header->freelist = (Item*)(page + sizeof(Page_header));
add_head(&free_bin[bid], header);
//build a list from the free chunks
p = (char*)(header->freelist);
for(i=0;i<num;i++)
{
Item* item = (Item*)p;
item->next = (Item*)(p + n);
p += n;
}
Item* lastitem = (Item*)(p-n);
lastitem->next = 0;

return page;
}

void* smallobj_malloc(size_t sz)
{
int bid;
void* p;
Item* item;
Page_header* page;
if(sz > LARGEOBJ) return NULL;
bid = get_binsize(sz);
if(bid < 0 || bid >= BINNUM)
return 0;
if(!free_bin[bid])
{
char* q = (char*)allocate_page(bid);
if(!q)
{
printf("error: no page allocated\n");
return 0;
}
}
page = free_bin[bid];
p = page->freelist;
item = page->freelist;
page->freelist = item->next;
page->nfree--;
if(!free_bin[bid]->freelist || page->nfree == 0) //remove current page from free_bin, and add it to full_bin
{
Page_header* p2 = (Page_header*)remove_head(&free_bin[bid]);
add_head(&full_bin[bid], p2);
}
return p;
}

void smallobj_free(void* p)
{
char* page_start = PAGE_START(p);
Mmap_alloc_header* phead = (Mmap_alloc_header*)page_start;
Page_header* page = (Page_header*)page_start;
size_t n = phead->nsize;
Item* item = (Item*)p;
int bid = get_binsize(n);
if(bid < 0 || bid >= BINNUM) return;
if(n > LARGEOBJ) return;

item->next = page->freelist; //add to head of free list
page->freelist = item;
page->nfree ++;
if(page->nfree == (PAGESIZE - sizeof(Page_header))/page->nsize)
{
remove_page(&free_bin[bid], page);
munmap(page_start, PAGESIZE);
}
}

void* largeobj_malloc(size_t sz)
{
if (sz <= 0) return NULL;
size_t len = sz + sizeof(Mmap_alloc_header);
char* p = (char*)mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(p == MAP_FAILED || !p){
printf("failed in mmap!\n");
return NULL;
}

Mmap_alloc_header *phead = (Mmap_alloc_header*)p;
phead->saddr = p;
phead->nsize = sz;

return (void*)(p + sizeof(Mmap_alloc_header));
}

void largeobj_free(void* p)
{
if(p == NULL) return;
Mmap_alloc_header* phead = (Mmap_alloc_header*)((char*)p - sizeof(Mmap_alloc_header));
munmap(phead->saddr, phead->nsize + sizeof(Mmap_alloc_header));
}

void* malloc(size_t sz)
{
void *p=0;
if(sz > LARGEOBJ)
p = largeobj_malloc(sz);
else
p = smallobj_malloc(sz);

DBG(printf("%p: allocate mem size %lu\n", p, sz);)
return p;
}

void free(void* p)
{
char* page_start = PAGE_START(p);
Mmap_alloc_header* phead = (Mmap_alloc_header*)page_start;
size_t n = phead->nsize;
if(n > LARGEOBJ)
largeobj_free(p);
else
smallobj_free(p);
DBG(printf("%p: free mem\n", p);)
}

 

---------------------------------------------------------------------------------

Testing:

[torstan]$ LD_PRELOAD=./libmymalloc.so ./a.out
malloc size 1, return 0x2a95659028
malloc size 2, return 0x2a95659030
malloc size 4, return 0x2a95659038
malloc size 8, return 0x2a95659040
malloc size 10, return 0x2a9565b028
malloc size 20, return 0x2a9565c028
malloc size 16, return 0x2a9565b038
malloc size 1000, return 0x2a9565d028
malloc size 1023, return 0x2a9565d428
malloc size 1024, return 0x2a9565d828
malloc size 2000, return 0x2a9565e010

free size 1, at 0x2a95659028
free size 2, at 0x2a95659030
free size 4, at 0x2a95659038
free size 8, at 0x2a95659040
free size 10, at 0x2a9565b028
free size 20, at 0x2a9565c028
free size 16, at 0x2a9565b038
free size 1000, at 0x2a9565d028
free size 1023, at 0x2a9565d428
free size 1024, at 0x2a9565d828
free size 2000, at 0x2a9565e010

 

---------------

random test code

[torstan]$ more randomtest.cc
#include<iostream>
#include<vector>
#include<cstdlib>
using namespace std;

int main()
{
int N=10000;
vector<int*> v;
v.reserve(N);
for(int i=0;i<N;i++)
{
v.push_back(0);
}

for(int i=0;i<N;i++)
{
int n = rand()%N;
int *p = v[n];
if(p == 0)
{
int sz = rand()%3000;
v[n] = new int[sz];
}
else
{
delete []p;
v[n] = 0;
}
}

for(int i=0;i<N;i++)
{
int *p = v[i];
if(p)
{
delete []p;
p = 0;
}
}

return 0;
}

 

[tli@bclnx64 memoryStudy]$ grep allocate testres | wc -l
7129
[tli@bclnx64 memoryStudy]$ grep free testres | wc -l
7129
[tli@bclnx64 memoryStudy]$ tail testres
0x2a97400010: free mem
0x2a9736e010: free mem
0x2a95da1010: free mem
0x2a95ee8010: free mem
0x2a96fd3010: free mem
0x2a967a3010: free mem
0x2a958b7010: free mem
0x2a962a8010: free mem
0x2a9609a010: free mem
0x2a95659010: free mem

posted on 2012-07-06 20:56  Torstan  阅读(217)  评论(0编辑  收藏  举报

导航