CSAPP:动态内存分配器

实验要求

实现一个堆上的动态内存分配器,达到最高性能

前期准备

  • 框架代码是一个最快的、最低效率的malloc,直接运行默认测试集得到66分(以下评分均基于默认测试集)
  • 书上给的代码是基于隐式空闲链表,使用立即边界标记合并首次适配方式的简单分配器,我在CSAPP官网下载了书上的代码,运行得到的分数是75分
    • alt text
  • 这里有个小插曲,无论是书上的代码还是网上已经实现的代码,直接运行都会显示“段错误”,经过某评论的启发,发现这里的代码应该是32位的,而我的环境是64位的,所以无法直接运行。解决方法如下
    • 执行sudo apt-get install gcc-multilib完善编译环境
    • Makefile中加上-m32
  • 根据书上内容,“因为块分配与堆块的总数呈线性关系,所以对于通用的分配器,隐式空闲链表是不合适的”,因此为了提高分配器的评分,我采用了分离适配的空闲链表算法,这种方法既快速,对内存的使用也很有效率
    • 这一方法也是C标准库中提供的GNU malloc包采用的算法

算法思路

  • 空闲块的header后存储两个指针,分别指向前一个空闲块和后一个空闲块(类似于双向链表)
    • alt text
  • 分离适配的空闲链表,以2的幂次为界划分成链表数组(数组具体长度需要调参确定最优值),每一个链表内空闲块的大小均按递增的顺序排列,因此可以采用首次适配的方式来选择空闲块
    • alt text
  • 当应用程序发起内存请求时,分配器首先检查是否有足够大的空闲块可用,如果有,则从相应的空闲链表中取出一个块进行分配;如果没有,它会扩展堆以创建一个足够大的块来满足请求
  • 当内存被释放时,释放的块会与相邻的空闲块合并,然后被重新链接到相应的空闲链表中,以保持内存的连续性和提高内存利用效率

优化思路

  • get_freelisthead,输出符合size大小的分离的空闲链表的起始位置
    • 常规的思路是用多个if判断语句来确定给定的size大小在哪个链表中
    • 稍微进阶一点的写法是利用下面这种三目运算符实现
     int i = (size <= 16)    ? 0 :
           (size <= 32)    ? 1 :
           (size <= 64)    ? 2 :
           (size <= 128)   ? 3 :
           (size <= 256)   ? 4 :
           (size <= 512)   ? 5 :
           (size <= 1024)  ? 6 :
           (size <= 2048)  ? 7 :
           (size <= 4096)  ? 8 : 9;
    
    • 更好的写法是利用移位运算来实现
	int shift = 4;
	while (shift < (SEG_LIST_COUNT + 3) && size > (1 << shift))
		shift++;
  • mm_realloc,重新分配
    • 常规的思路就是调用mm_malloc分配一个新空间,然后使用memcpy将原块中的数据复制过去,在把原来的块mm_free,但是其中复制数据的过程其实很浪费时间,如何避免复制数据?
      • 尽量保证数据不动,若空间不够可以把下一个空闲块拼接起来
    • 考虑下面三种情况
      • Case1:若原块大小更大,则返回原指针(若剩余空间超过最小块大小,将剩余空间回收)
      • Case2:若原块大小更小,考虑下一个块是否为空闲块,若加上下一个空闲块能满足要求,则把两个块拼接起来,返回原指针(若剩余空间超过最小块大小,将剩余空间回收)
      • Case3:若原块大小更小,且不能通过拼接下一个块达到要求,只能重新分配一个空间,复制原数据,再释放原空间

实验结果

  • 最终的评分是91分,比框架代码提高了37.9%
    • alt text

参考链接

CSAPP:Lab5-Malloc Lab - 知乎
六 Malloc Lab - 简书
GitHub - hehozo/Malloc-lab: CSAPP Malloc-lab: 91% performance index, my own dynamic memory allocator in C.
与 Malloc Lab(in csapp) 大战三天三夜纪实 - 知乎
CSapp lab5 MAlloc Lab以及段错误处理 && CSapp第九章学习 2 Linux虚拟内存系统与Linux进程虚拟地址空间 - TheDa - 博客园
CSAPP-malloclab 解题思路记录 - 找一个吃麦旋风的理由

完整代码

// 本实验采用分离适配的空闲链表算法,这是一种高效的内存管理技术,它通过维护多个不同大小的空闲链表来优化内存的分配和回收过程。
// 该算法将内存划分成一系列固定大小的块,这些块可以进一步细分以适应不同大小的内存请求。每个空闲链表根据块的大小进行分类,
// 每个分类中包含相同大小区间的空闲块。当应用程序发起内存请求时,分配器首先检查是否有足够大的空闲块可用,
// 如果有,则从相应的空闲链表中取出一个块进行分配;如果没有,它会扩展堆以创建一个足够大的块来满足请求。
// 当内存被释放时,释放的块会与相邻的空闲块合并,然后被重新链接到相应的空闲链表中,以保持内存的连续性和提高内存利用效率。
// 这种算法的优点在于其快速响应内存请求的能力,并通过合并空闲块减少了内存碎片,从而显著提升了内存的使用效率。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <string.h>

#include "mm.h"
#include "memlib.h"

/*********************************************************
 * 亲们请注意:开始之前,请把下面的信息修改为你的个人信息
 ********************************************************/
team_t team = {
   
    /* 团队名字 */
    "Lane",
    /* 团队老大的名字 */
    "Lane",
    /* 团队老大的email地址 */
    "[email protected]",
    /* 团队其他成员的名字 (如果没有,就空着) */
    "",
    /* 团队其他成员的email地址 (如果没有,就空着) */
    ""};

/* 常数和宏定义 */
#define WSIZE 4             // 字(bytes)
#define DSIZE 8             // 双字(bytes)
#define CHUNKSIZE (1 << 12) // 按此数量(字节)扩展堆
#define SEG_LIST_COUNT 22   // 分离的空闲链表长度(对不同测试集其最优值不同,需要调参)

#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) = (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(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值