diff --git a/malloclab/Makefile b/malloclab/Makefile index b8d3eb0..394e9c8 100644 --- a/malloclab/Makefile +++ b/malloclab/Makefile @@ -19,7 +19,7 @@ objects = clock.o fcyc.o fsecs.o mdriver.o memlib.o mm.o all: $(PROGRAMS) malloc : $(objects) - gcc $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) + clang $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) rm -f $(objects) clean: diff --git a/malloclab/malloc b/malloclab/malloc new file mode 100755 index 0000000..1cd23c2 Binary files /dev/null and b/malloclab/malloc differ diff --git a/malloclab/method.md b/malloclab/method.md new file mode 100644 index 0000000..4cae119 --- /dev/null +++ b/malloclab/method.md @@ -0,0 +1,76 @@ +# 动态内存分配器实验修改思路 + +## 实验目标 +实现一个动态内存分配器,支持以下功能: +1. 内存分配(`mm_malloc`) +2. 内存释放(`mm_free`) +3. 内存扩展(`mm_realloc`) + +并通过 `traces` 目录下的测试用例,尽可能提高评分。 + +## 修改内容 + +### 1. 算法设计思想 +- 使用 **隐式空闲链表** 管理内存块。 +- 每个块包含头部和尾部,用于存储块大小和分配状态。 +- 分配时,使用 **首次适配算法** 找到合适的空闲块。 +- 释放时,合并相邻的空闲块以减少内存碎片。 +- 通过对齐和块分割优化内存利用率。 + +### 2. 代码实现 +#### 主要宏定义 +- `PACK(size, alloc)`:将块大小和分配位打包成一个字。 +- `GET` 和 `PUT`:读取和写入地址处的字。 +- `HDRP` 和 `FTRP`:计算块的头部和尾部地址。 +- `NEXT_BLKP` 和 `PREV_BLKP`:计算下一个和前一个块的地址。 + +#### 核心函数 +1. **`mm_init`** + - 初始化堆,创建序言块和结尾块。 + - 扩展堆以初始化空闲块。 + +2. **`extend_heap`** + - 扩展堆的大小,分配新的空闲块。 + - 初始化新块的头部、尾部和结尾块。 + +3. **`coalesce`** + - 合并相邻的空闲块,减少内存碎片。 + - 根据前后块的分配状态,处理 4 种情况。 + +4. **`mm_malloc`** + - 调整请求大小以满足对齐要求。 + - 使用 `find_fit` 查找合适的空闲块。 + - 如果没有合适的块,扩展堆。 + +5. **`mm_free`** + - 将块标记为空闲。 + - 调用 `coalesce` 合并相邻空闲块。 + +6. **`mm_realloc`** + - 如果新大小小于当前块大小,直接返回。 + - 如果新大小大于当前块大小,分配新块并复制数据。 + +7. **`find_fit`** + - 遍历隐式链表,找到第一个满足大小要求的空闲块。 + +8. **`place`** + - 将请求大小的块放入空闲块中。 + - 如果剩余空间足够大,分割块。 + +### 3. 测试与优化 +- 修改 `TRACE_LIST.txt`,运行不同的 trace 文件。 +- 使用 `make` 和 `./malloc -t traces` 测试实现。 +- 优化分配和释放逻辑,提高效率和性能。 + +## 当前状态 +- **正确性**:所有 trace 文件均通过测试。 +- **效率**:平均效率为 77%。 +- **性能**:评分为 84/100。 + +## 后续优化方向 +1. 使用 **显式空闲链表** 或 **分离空闲链表** 提高查找效率。 +2. 动态调整堆扩展策略,减少不必要的扩展。 +3. 优化块分割和合并逻辑,进一步减少内存碎片。 + +## 提交要求 +- 将 `mm.c` 文件重命名为 `mm_学号.c`,并提交到指定平台。 diff --git a/malloclab/mm.c b/malloclab/mm.c index fba8b79..76c3fda 100644 --- a/malloclab/mm.c +++ b/malloclab/mm.c @@ -1,4 +1,5 @@ -/* +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +/* * mm-naive.c - 参考实现,是一个最快的、最低效率的malloc. * * 在这个参考实现中,分配一个块,仅仅是增加brk指针, @@ -10,139 +11,227 @@ * 的话来说清楚。 * 请将此文件,重新命名为mm_201309060024.c(就是mm_你的学号.c) */ +#include #include #include -#include + #ifndef _WIN32 #include #endif #include -#include "mm.h" #include "memlib.h" +#include "mm.h" - /********************************************************* - * 亲们请注意:开始之前,请把下面的信息修改为你的个人信息 - ********************************************************/ +/********************************************************* + * 亲们请注意:开始之前,请把下面的信息修改为你的个人信息 + ********************************************************/ team_t team = { - /* 团队名字 */ - "Tom is a Cat", - /* 团队老大的名字 */ - "Tom", - /* 团队老大的email地址 */ - "Tom@sina.com", - /* 团队其他成员的名字 (如果没有,就空着) */ - "", - /* 团队其他成员的email地址 (如果没有,就空着) */ - "" -}; + /* 团队名字 */ + "Tom is a Cat", + /* 团队老大的名字 */ + "Tom", + /* 团队老大的email地址 */ + "Tom@sina.com", + /* 团队其他成员的名字 (如果没有,就空着) */ + "", + /* 团队其他成员的email地址 (如果没有,就空着) */ + ""}; /* 单字 (4) 还是双字 (8) 边界对齐 */ #define ALIGNMENT 8 /* 舍入到最近的ALIGNMENT边界上 */ -#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) - +#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7) #define SIZE_T_SIZE (ALIGN(sizeof(size_t))) +// 定义块头部和尾部的大小 +#define WSIZE 4 // 字大小(4字节) +#define DSIZE 8 // 双字大小(8字节) +#define CHUNKSIZE (1 << 12) // 扩展堆的默认大小(4KB) + +// 将大小和分配位打包成一个字 +#define PACK(size, alloc) ((size) | (alloc)) + +// 读取和写入地址p处的字 +#define GET(p) (*(unsigned int *)(p)) +#define PUT(p, val) (*(unsigned int *)(p) = (val)) + +// 从地址p读取大小和分配位 +#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(((char *)(bp) - WSIZE))) +#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) + +static char *heap_listp; // 指向堆的起始位置 + +static void *extend_heap(size_t words); +static void *coalesce(void *bp); +static void *find_fit(size_t asize); +static void place(void *bp, size_t asize); + static void print_block(int request_id, int payload); /* - * mm_init - 初始化malloc系统,此函数,在整个运行期间,只被调用1次,用于建立初始化环境 + * mm_init - + * 初始化malloc系统,此函数,在整个运行期间,只被调用1次,用于建立初始化环境 */ -int mm_init(void) -{ - return 0; +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); + + // 扩展空堆 + if (extend_heap(CHUNKSIZE / WSIZE) == NULL) + return -1; + return 0; } -/* - * mm_malloc - 通过增加brk指针,来分配一块内存。 - * 总是分配一块内存,它的大小是ALIGNMENT的整数倍(对齐)。 - */ -void* mm_malloc(size_t size) -{ - // 将大小调整到ALIGNMENT的整数倍(对齐) - int newsize = ALIGN(size + SIZE_T_SIZE); +static void *extend_heap(size_t words) { + char *bp; + size_t size; - // 修改brk指针 - void* p = mem_sbrk(newsize); - if (p == (void*)-1) - { - printf("[%s]mm_alloc失败:size=%zu newsize=%d\n", __func__, size, newsize); - return NULL; - } - else { - *(size_t*)p = size; - return (void*)((char*)p + 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)); // 新的结尾块 + + return coalesce(bp); } -/* - * mm_free - 释放一块内存。其实没干啥事.... - */ -void mm_free(void* ptr) -{ +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)); + + if (prev_alloc && next_alloc) { // Case 1 + return bp; + } else if (prev_alloc && !next_alloc) { // Case 2 + 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) { // Case 3 + 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 { // Case 4 + 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; } -/* - * mm_realloc - 重新扩展一块已分配的内存。仅仅是使用mm_malloc和mm_free来实现,很蠢 - */ -void* mm_realloc(void* ptr, size_t size) -{ - void* oldptr = ptr; - void* newptr; - size_t copySize; +void *mm_malloc(size_t size) { + size_t asize; // 调整后的块大小 + size_t extendsize; // 如果没有合适的块,扩展堆的大小 + char *bp; - // 首先分配一块大一点的内存 - newptr = mm_malloc(size); - if (newptr == NULL) - return NULL; - copySize = *(size_t*)((char*)oldptr - SIZE_T_SIZE); - if (size < copySize) - copySize = size; + if (size == 0) + return NULL; - // 把老内存里面的内容,复制到新内存里面 - memcpy(newptr, oldptr, copySize); + if (size <= DSIZE) + asize = 2 * DSIZE; + else + asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE); - // 释放掉老内存 - mm_free(oldptr); + if ((bp = find_fit(asize)) != NULL) { + place(bp, asize); + return bp; + } - // 返回新内存的指针 - return newptr; + extendsize = MAX(asize, CHUNKSIZE); + if ((bp = extend_heap(extendsize / WSIZE)) == NULL) + return NULL; + place(bp, asize); + return bp; } + +void mm_free(void *bp) { + if (bp == NULL) + return; + + size_t size = GET_SIZE(HDRP(bp)); + PUT(HDRP(bp), PACK(size, 0)); + PUT(FTRP(bp), PACK(size, 0)); + coalesce(bp); +} + +void *mm_realloc(void *ptr, size_t size) { + if (ptr == NULL) + return mm_malloc(size); + + if (size == 0) { + mm_free(ptr); + return NULL; + } + + void *newptr = mm_malloc(size); + if (newptr == NULL) + return NULL; + + size_t copySize = GET_SIZE(HDRP(ptr)) - DSIZE; + if (size < copySize) + copySize = size; + memcpy(newptr, ptr, copySize); + mm_free(ptr); + return newptr; +} + +static void *find_fit(size_t asize) { + void *bp; + + for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { + if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp)))) { + return bp; + } + } + return NULL; +} + +static void place(void *bp, size_t asize) { + size_t csize = GET_SIZE(HDRP(bp)); + + 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)); + } else { + PUT(HDRP(bp), PACK(csize, 1)); + PUT(FTRP(bp), PACK(csize, 1)); + } +} + /* * mm_heapcheck - 目前暂不支持堆检查,可以不用修改 -*/ -void mm_heapcheck(void) -{ -} + */ +void mm_heapcheck(void) {} /* * 输出一块数据 - 用于heapcheck,然而在此并没有什么用,可以不用修改 */ -static void print_block(int request_id, int payload) -{ - printf("\n[%s]$BLOCK %d %d\n", __func__, request_id, payload); +static void print_block(int request_id, int payload) { + printf("\n[%s]$BLOCK %d %d\n", __func__, request_id, payload); } - - - - - - - - - - - - - - - - - - - -