这是一个本人学习 csapp 的 learning 库
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

5.5 KiB

关于示例代码的理解

环境

  • mem_init 中可以看出整个分配器是在一大段已经分配好的连续的内存中进行的
  • 同样每次 mem_sbrk 进行扩展时也是连续扩展,模拟堆的向上增长

对齐

  • 标准 malloc 也是八字节对齐,所以可以满足强制八字节对齐的要求
  • 返回的内存应为有效载荷部分,如果每个块加上头部和脚部,为了满足对齐要求,须在开头空出四字节的位置
  • 也意味着空闲块最小为八字节大小,有效载荷也应八字节对齐,已分配块最小十六字节

测试

  • handout 里的测试用例偏少,从网上找到了更为详尽的 traces 测试用例来测试

version 1

规则与注意

理解逻辑后,接下来将实现自己的版本

  • 为方便理解,定义了两个类型别名
    typedef unsigned int word;
    typedef char byte;
    
  • 不同于示例代码用序言块和尾块标记,本人仅用两个指针标记头尾,来提高内存利用率
    // mark the front and tail pos
    void *front_p = NULL;
    void *tail_p = NULL;
    
    • 但会增加代码的复杂度,需着重维护
    • 同时还应注意若 bp == front_pPREV(bp) 内的值无效,tail_p 同理
  • 为保持一致性,向辅助函数内传入的 size 均应在传入之前对齐,均不包含头尾部大小
  • 仅在内部碎片大于等于十六字节时才进行切割
  • 其他部分与示例大同小异

bug 与 debug

  • #debug对于 segmentation fault 使用 gdb 获取头尾块的 size 发现尾部异常值 0xcdcdcd,在代码中使用 print 跟踪 trail_p 变量,发现在__coalesce_next处没有及时更新
  • #bug1 若记录的 size 是有效载荷的 size,合并和分割时应注意增减 DSIZE
  • #bug2 每次合并都需要判断 tail_p 是否改变,特别是 __coalesce_next 的情况

方法与得分

  • 隐式空闲链表,首次适配,立即合并
Results for mm malloc:
trace  valid  util     ops      secs  Kops
 0       yes   99%    5694  0.007579   751
 1       yes  100%    5848  0.006639   881
 2       yes   99%    6648  0.010560   630
 3       yes  100%    5380  0.008016   671
 4       yes  100%   14400  0.000102140762
 5       yes   92%    4800  0.006677   719
 6       yes   92%    4800  0.005988   802
 7       yes   55%   12000  0.141468    85
 8       yes   51%   24000  0.274197    88
 9       yes   33%   14401  0.128358   112
10       yes   50%   14401  0.002138  6734
Total          79%  112372  0.591722   190

Perf index = 47 (util) + 13 (thru) = 60/100

Version 1.1

针对 realloc 的优化 v1

  • new_size <= old_size 则不分配而是切割
  • 若下一块未分配且总和大于 new_size 则合并
  • 若合并后内部碎片过大则仍需分割
  • 提高六分,但判断过多,且考虑不全

针对 realloc 的优化 v2

  • 评估前后空闲块的总大小,若足够,则合并
  • 合并不会破坏数据,合并后复制数据,再根据需要分割
  • 然而针对第九项测试,合并前后空闲块反而内存利用率低于仅合并后空闲块

针对 realloc 的优化 v3

  • 最终选择的是逐步的过程,因为从时间开销上来看,直接返回优于仅合并后部分优于合并前后部分,但同时合并前后部分与再分配一段内存的优劣不好比较
  • 现在的问题是在仅合并后部分和重新分配之间要不要插一段合并前后部分的条件,两者分数相同,个人认为插入这个条件通用性更好

得分

Results for mm malloc:
trace  valid  util     ops      secs  Kops
 0       yes   99%    5694  0.007401   769
 1       yes  100%    5848  0.006883   850
 2       yes   99%    6648  0.011138   597
 3       yes  100%    5380  0.008327   646
 4       yes  100%   14400  0.000092156013
 5       yes   92%    4800  0.006244   769
 6       yes   92%    4800  0.005888   815
 7       yes   55%   12000  0.142196    84
 8       yes   51%   24000  0.277304    87
 9       yes   50%   14401  0.018129   794
10       yes   86%   14401  0.000132108933
Total          84%  112372  0.483734   232

Perf index = 50 (util) + 15 (thru) = 66/100

Version 1.2

使用 next fit

  • 因为大块的空闲块总是趋向于在后面,所以下一次适配不用从头遍历,可以大幅提高吞吐率
  • 引入新的全局变量 fitted_p 并且需要在多处维护:初始化,分配,合并

结合 best fit

  • next_fit 会降低内存利用率,而 best_fit 会降低吞吐率,但我们可以进行一个折衷,即在 fitted_p 后部分首次适配,而在 fitted_p 前部分最佳适配
  • 而最佳适配仅需找到满足要求的最小值即可
  • 然而因为前面的内存碎片太多,测试下来内存利用率的确有所提高,但是吞吐率下降的却更多,所以最后还是选择了 _next_fit

得分

Results for mm malloc:
trace  valid  util     ops      secs  Kops
 0       yes   91%    5694  0.001803  3158
 1       yes   92%    5848  0.001315  4446
 2       yes   97%    6648  0.003706  1794
 3       yes   97%    5380  0.003602  1494
 4       yes  100%   14400  0.000085169213
 5       yes   91%    4800  0.004207  1141
 6       yes   90%    4800  0.003837  1251
 7       yes   55%   12000  0.057487   209
 8       yes   51%   24000  0.029497   814
 9       yes   50%   14401  0.054370   265
10       yes   70%   14401  0.000116124684
Total          80%  112372  0.160025   702

Perf index = 48 (util) + 40 (thru) = 88/100

2022.12.29 ~ 2022.12.30