做malloclab首先要熟悉课本9.9的内容,尤其是9.9.12,如果不知道从哪里入手,可以和我一样,从实现课本介绍的简单分配器开始,然后在这个基础上改编。
试验后最终采取的是隐式空闲链表+分离的显式空闲链表,分离存储的块大小为{16-32},{33-64},{65-128}···,空闲块在链表中按从小到大排列,这样首次匹配的结果接近最佳匹配。
mm.h
#include
extern int mm_init (void);
extern void *mm_malloc (size_t size);
extern void mm_free (void *ptr);
extern void *mm_realloc(void *ptr, size_t size);
/*
* Students work in teams of one or two. Teams enter their team name,
* personal names and login IDs in a struct of this
* type in their bits.c file.
*/
typedef struct {
char *teamname; /* ID1+ID2 or ID1 */
char *name1; /* full name of first member */
char *id1; /* login ID of first member */
char *name2; /* full name of second member (if any) */
char *id2; /* login ID of second member */
} team_t;
extern team_t team;
#define WSIZE 4
#define DSIZE 8
#define MAX(x,y) ((x)>(y)?(x):(y))
#define PACK(size,alloc) ((size)|(alloc))
#define GET(p) (*(unsigned int*) (p))
#define PUT(p,val) (*(unsigned int *)(p)=(unsigned int)(val))
#define GET_SIZE(p) (GET(p)&~0x7)
#define GET_ALLOC(p) (GET(p)&0x1)
#define HDRP(bp) ((char*)(bp)-WSIZE)
#define FTRP(bp) ((char*)(bp)+GET_SIZE(HDRP(bp))-DSIZE)
#define NEXT_BLKP(bp) ((char*)(bp)+GET_SIZE(HDRP(bp)))
#define PREV_BLKP(bp) ((char*)(bp)-GET_SIZE((char*)(bp)-DSIZE))
#define NEXT_PTR(bp) ((char*)(bp))//空闲链表下一个地址的地址
#define PREV_PTR(bp) ((char*)(bp)+WSIZE)//空闲链表上一个地址的地址
#define NEXT(bp) (*(char**)(NEXT_PTR(bp)))//空闲链表下一个块地址
#define PREV(bp) (*(char**)(PREV_PTR(bp)))//空闲链表上一个块地址
mm.c
/*
* mm-naive.c - The fastest, least memory-efficient malloc package.
*
* In this naive approach, a block is allocated by simply incrementing
* the brk pointer. A block is pure payload. There are no headers or
* footers. Blocks are never coalesced or reused. Realloc is
* implemented directly using mm_malloc and mm_free.
*
* NOTE TO STUDENTS: Replace this header comment with your own header
* comment that gives a high level description of your solution.
*/
#include
#include
#include
#include
#include
#include "mm.h"
#include "memlib.h"
/*********************************************************
* NOTE TO STUDENTS: Before you do anything else, please
* provide your team information in the following struct.
********************************************************/
team_t team = {
/* Team name */
"ateam",
/* First member's full name */
"Shuyu",
/* First member's email address */
"[email protected]",
/* Second member's full name (leave blank if none) */
"",
/* Second member's email address (leave blank if none) */
""
};
/* single word (4) or double word (8) alignment */
#define ALIGNMENT 8
#define CHUNKSIZE 1<<12 //CHUNKSIZE一定要>=2*DSIZE
/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define LISTLEN 20 //分离的空闲链表个数
static void* heap_listp;
static void* segregated_header[LISTLEN]; //分离空闲链表
static void* extend_heap(size_t words); //拓展堆words个字
static void* coalesce(void* bp); //如果bp前后相邻空闲块,合并之
static void* find_fit(size_t asize); //寻找能够容纳asize字节的空闲块(初次匹配)
static void place(void* bp,size_t asize); //在bp指向的空闲块中分配asize空间,如果空闲块剩余空间>=2*DSIZE,将其再插入合适的空闲链表中
static int list_index(size_t asize); //返回asize大小的空闲块所在空闲链表的索引
static void insert_free(void* bp,size_t asize); //插入bp指向的asize大小的空闲块到空闲链表中
static void delete_free(void* bp); //在空闲链表中删除bp指向的空闲块
/*
* mm_init - initialize the malloc package.
*/
int mm_init(void)
{
//初始化空堆
if((heap_listp=mem_sbrk(4*WSIZE))==(void*)-1)
return -1;
PUT(heap_listp,0);
PUT(heap_listp+(1*WSIZE),PACK(DSIZE,1));//序言块
PUT(heap_listp+(2*WSIZE),PACK(DSIZE,1));//序言块
PUT(heap_listp+(3*WSIZE),PACK(0,1));//结尾块
heap_listp+=2*WSIZE;
//初始化分离链表
for(int i=0;i<LISTLEN;i++)
segregated_header[i]=NULL;
//扩展堆
if(extend_heap(CHUNKSIZE/WSIZE)==NULL)
return -1;
return 0;
}
/*
* mm_malloc - Allocate a block by incrementing the brk pointer.
* Always allocate a block whose size is a multiple of the alignment.
*/
void *mm_malloc(size_t size)
{
size_t asize; //分配块的实际大小
size_t extendsize;
char *bp;
//计算分配空间的实际大小asize,不小于2*DSIZE,要对齐
if(size==0)
return NULL;
if(size<=DSIZE)
asize=2*DSIZE;
else
asize=DSIZE*((size+(DSIZE)+(DSIZE-1))/DSIZE);
//找到合适的空闲块并分配
if((bp=find_fit(asize))!=NULL){
place(bp,asize);
return bp;
}
//如果没有合适的空闲块,那么扩展堆,再分配空间
extendsize=MAX(asize,CHUNKSIZE);
if((bp=extend_heap(extendsize/WSIZE))==NULL)
return NULL;
place(bp,asize);
return bp;
}
/*
* mm_free - Freeing a block does nothing.
*/
void mm_free(void *ptr)
{
//置为空闲块
PUT(HDRP(ptr),PACK(GET_SIZE(HDRP(ptr)),0));
PUT(FTRP(ptr),PACK(GET_SIZE(HDRP(ptr)),0));
//如果bp前后相邻空闲块,合并之
ptr=coalesce(ptr);
//插入到空闲链表
insert_free(ptr,GET_SIZE(HDRP(ptr)));
}
/*
* mm_realloc - Implemented simply in terms of mm_malloc and mm_free
*/
void *mm_realloc(void *ptr, size_t size)
{
void *oldptr = ptr;
void *newptr;
size_t asize;
//处理ptr==NULL和size==0的情况
if(ptr==NULL)
return mm_malloc(size);
if(size==0){
insert_free(ptr,GET_SIZE(HDRP(ptr)));
return NULL;
}
//计算分配空间的实际大小asize,不小于2*DSIZE,要对齐
if(size<=DSIZE)
asize=2*DSIZE;
else
asize=DSIZE*((size+(DSIZE)+(DSIZE-1))/DSIZE);
size_t copySize,oldsize;
//空间的原大小oldsize
oldsize=GET_SIZE(HDRP(ptr));
//如果原空间足够大,仍然在原空间分配
if(oldsize>=asize){
place(ptr,asize);
newptr=ptr;
}
//如果原空间不够大
else{
//试合并前后块
ptr=coalesce(ptr);
//如果合并后能够容纳asize,则在合并后的空间中分配
if(GET_SIZE(HDRP(ptr))>=asize){
newptr=ptr;
if(newptr!=oldptr){//如果向前合并了,需要拷贝数据
copySize=oldsize-DSIZE;
if (size < copySize)
copySize = size;
memmove(newptr, oldptr, copySize);//memcpy无法处理重叠情况,所以用memmove
}
place(newptr,asize);
}
//如果合并后仍然不够容纳asize,则malloc新空间,拷贝数据,释放原空间
else{
newptr = mm_malloc(size);
if (newptr == NULL)
return NULL;
copySize = oldsize-DSIZE;
if (size < copySize)
copySize = size;
memcpy(newptr, oldptr, copySize);
insert_free(ptr,GET_SIZE(HDRP(ptr)));
}
}
return newptr;
}
static void* extend_heap(size_t words){
char *bp;
size_t size;
size=((words%2)?(words+1)*WSIZE:words*WSIZE);
if((long)(bp=mem_sbrk(size))==-1)
return NULL;
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));//设置头尾
PUT(HDRP(NEXT_BLKP(bp)),PACK(0,1));//结尾块
bp=coalesce(bp);//合并空闲块
insert_free(bp,GET_SIZE(HDRP(bp)));//插入空闲链表
return bp;
}
static void* coalesce(void* bp){
size_t prev_alloc=GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next_alloc=GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size=GET_SIZE(HDRP(bp));
//根据前后块分配情况分四种情况设置头、尾,但不把新块插入空闲队列(因为realloc用到此函数,插入空闲队列的操作会破坏原数据)
if(prev_alloc && next_alloc){
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
}
else if(prev_alloc&&!next_alloc){
delete_free(NEXT_BLKP(bp));
size+=GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
}
else if(!prev_alloc&&next_alloc){
delete_free(PREV_BLKP(bp));
size+=GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
bp=PREV_BLKP(bp);
}
else{
delete_free(PREV_BLKP(bp));
delete_free(NEXT_BLKP(bp));
size+=GET_SIZE(HDRP(PREV_BLKP(bp)))+GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0));
bp=PREV_BLKP(bp);
}
return bp;
}
static void* find_fit(size_t asize){
void* bp;
for(int i=list_index(asize);i<LISTLEN;i++){//从asize对应的空闲链表开始向后查找
if(segregated_header[i]){
for(bp=segregated_header[i];bp;bp=NEXT(bp))
if(asize<=GET_SIZE(HDRP(bp)))
return bp;//首次匹配
}
}
return NULL;
}
static void place(void* bp,size_t asize){
if(!GET_ALLOC(FTRP(bp)))
delete_free(bp);
size_t csize=GET_SIZE(HDRP(bp));
//剩余空间如果大于等于最小块(2*DSIZE),则将剩余空间置空闲块
if((csize-asize)>=(2*DSIZE)){
PUT(HDRP(bp),PACK(asize,1));
PUT(FTRP(bp),PACK(asize,1));
bp=NEXT_BLKP(bp);
PUT(HDRP(bp),PACK(csize-asize,0));
PUT(FTRP(bp),PACK(csize-asize,0));
insert_free(bp,csize-asize);
}
else{
PUT(HDRP(bp),PACK(csize,1));
PUT(FTRP(bp),PACK(csize,1));
}
}
static int list_index(size_t asize){
int size=16;//分离链表:[8~16][17~32][33~64]...
for(int i=0;i<LISTLEN;i++){
if(size>=asize) return i;
size=size<<1;
}
return LISTLEN-1;
}
static void insert_free(void* bp,size_t asize){//将asize的空闲块放入相应的空闲链表中,按从小到大的顺序存放
PUT(HDRP(bp),PACK(asize,0));
PUT(FTRP(bp),PACK(asize,0));//设置头尾
int listi=list_index(asize);
void* p=segregated_header[listi];//listi:对应链表头结点
void* pre=NULL;//查找中的前驱结点指针
if(segregated_header[listi]==NULL){//如果对应链表为空,那么直接插入bp为头结点
PUT(NEXT_PTR(bp),NULL);
PUT(PREV_PTR(bp),0);
segregated_header[listi]=bp;
}
else{//如果链表不为空
while(p&&GET_SIZE(HDRP(p))<asize){
pre=p;
p=NEXT(p);
}//遍历链表直到结尾或找到大于等于asize的结点p
if(pre==NULL)
segregated_header[listi]=bp;//如果pre==NULL,说明头结点就大于等于asize,那么bp应该是新的头结点
else
PUT(NEXT_PTR(pre),bp);//否则如果pre!=NULL,那么bp是pre的后继
PUT(NEXT_PTR(bp),p);//p是bp的后继
PUT(PREV_PTR(bp),pre);//pre是bp的前驱
if(p) PUT(PREV_PTR(p),bp);//bp是p的前驱
}
}
static void delete_free(void* bp){
int listi=list_index(GET_SIZE(HDRP(bp)));//listi:对应链表头结点
if(NEXT(bp)==NULL&&PREV(bp)==NULL){//如果bp是链表上的唯一结点,删除该结点,置对应链表为空
segregated_header[listi]=NULL;
}
else if(PREV(bp)==NULL){//如果bp是头结点,置头结点为bp的后继结点
segregated_header[listi]=NEXT(bp);
PUT(PREV_PTR(NEXT(bp)),0);
}
else if(NEXT(bp)==NULL){//如果bp是末尾结点,置前驱结点为末尾结点
PUT(NEXT_PTR(PREV(bp)),0);
}
else{//如果bp是中间结点,连接它的前驱和后继结点
PUT(PREV_PTR(NEXT(bp)),PREV(bp));
PUT(NEXT_PTR(PREV(bp)),NEXT(bp));
}
}
最后测试的成绩是88分
Team Name:ateam
Member 1 :Shuyu:[email protected]
Using default tracefiles in /home/hazel/malloclab-handout/traces/
Measuring performance with gettimeofday().
Results for mm malloc:
trace valid util ops secs Kops
0 yes 99% 5694 0.000386 14740
1 yes 99% 5848 0.000291 20110
2 yes 99% 6648 0.000338 19657
3 yes 100% 5380 0.000281 19119
4 yes 99% 14400 0.000321 44916
5 yes 96% 4800 0.000760 6317
6 yes 96% 4800 0.000717 6695
7 yes 55% 6000 0.000220 27236
8 yes 51% 7200 0.000246 29304
9 yes 40% 14401 0.000866 16622
10 yes 46% 14401 0.000493 29217
Total 80% 89572 0.004919 18208
Perf index = 48 (util) + 40 (thru) = 88/100