这是一个本人学习 csapp 的 learning 库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
5.5 KiB

  1. ## 关于示例代码的理解
  2. #### 环境
  3. *`mem_init` 中可以看出整个分配器是在一大段已经分配好的连续的内存中进行的
  4. * 同样每次 `mem_sbrk` 进行扩展时也是连续扩展,模拟堆的向上增长
  5. #### 对齐
  6. * 标准 `malloc` 也是八字节对齐,所以可以满足强制八字节对齐的要求
  7. * 返回的内存应为有效载荷部分,如果每个块加上头部和脚部,为了满足对齐要求,须在开头空出四字节的位置
  8. * 也意味着空闲块最小为八字节大小,有效载荷也应八字节对齐,已分配块最小十六字节
  9. #### 测试
  10. * handout 里的测试用例偏少,从网上找到了更为详尽的 `traces` 测试用例来测试
  11. ## version 1
  12. #### 规则与注意
  13. 理解逻辑后,接下来将实现自己的版本
  14. * 为方便理解,定义了两个类型别名
  15. ```c
  16. typedef unsigned int word;
  17. typedef char byte;
  18. ```
  19. * 不同于示例代码用序言块和尾块标记,本人仅用两个指针标记头尾,来提高内存利用率
  20. ```c
  21. // mark the front and tail pos
  22. void *front_p = NULL;
  23. void *tail_p = NULL;
  24. ```
  25. * 但会增加代码的复杂度,需着重维护
  26. * 同时还应注意若 `bp == front_p``PREV(bp)` 内的值无效,`tail_p` 同理
  27. * 为保持一致性,向辅助函数内传入的 `size` 均应在传入之前对齐,均不包含头尾部大小
  28. * 仅在内部碎片大于等于十六字节时才进行切割
  29. * 其他部分与示例大同小异
  30. #### bug 与 debug
  31. * `#debug`对于 `segmentation fault` 使用 `gdb` 获取头尾块的 `size` 发现尾部异常值 `0xcdcdcd`,在代码中使用 `print` 跟踪 `trail_p` 变量,发现在`__coalesce_next`处没有及时更新
  32. * `#bug1` 若记录的 `size` 是有效载荷的 `size`,合并和分割时应注意增减 `DSIZE`
  33. * `#bug2` 每次合并都需要判断 `tail_p` 是否改变,特别是 `__coalesce_next` 的情况
  34. #### 方法与得分
  35. * 隐式空闲链表,首次适配,立即合并
  36. ```c
  37. Results for mm malloc:
  38. trace valid util ops secs Kops
  39. 0 yes 99% 5694 0.007579 751
  40. 1 yes 100% 5848 0.006639 881
  41. 2 yes 99% 6648 0.010560 630
  42. 3 yes 100% 5380 0.008016 671
  43. 4 yes 100% 14400 0.000102140762
  44. 5 yes 92% 4800 0.006677 719
  45. 6 yes 92% 4800 0.005988 802
  46. 7 yes 55% 12000 0.141468 85
  47. 8 yes 51% 24000 0.274197 88
  48. 9 yes 33% 14401 0.128358 112
  49. 10 yes 50% 14401 0.002138 6734
  50. Total 79% 112372 0.591722 190
  51. Perf index = 47 (util) + 13 (thru) = 60/100
  52. ```
  53. ## Version 1.1
  54. #### 针对 `realloc` 的优化 v1
  55. *`new_size <= old_size` 则不分配而是切割
  56. * 若下一块未分配且总和大于 `new_size` 则合并
  57. * 若合并后内部碎片过大则仍需分割
  58. * 提高六分,但判断过多,且考虑不全
  59. #### 针对 `realloc` 的优化 v2
  60. * 评估前后空闲块的总大小,若足够,则合并
  61. * 合并不会破坏数据,合并后复制数据,再根据需要分割
  62. * 然而针对第九项测试,合并前后空闲块反而内存利用率低于仅合并后空闲块
  63. #### 针对 `realloc` 的优化 v3
  64. * 最终选择的是逐步的过程,因为从时间开销上来看,直接返回优于仅合并后部分优于合并前后部分,但同时合并前后部分与再分配一段内存的优劣不好比较
  65. * 现在的问题是在仅合并后部分和重新分配之间要不要插一段合并前后部分的条件,两者分数相同,个人认为插入这个条件通用性更好
  66. #### 得分
  67. ```c
  68. Results for mm malloc:
  69. trace valid util ops secs Kops
  70. 0 yes 99% 5694 0.007401 769
  71. 1 yes 100% 5848 0.006883 850
  72. 2 yes 99% 6648 0.011138 597
  73. 3 yes 100% 5380 0.008327 646
  74. 4 yes 100% 14400 0.000092156013
  75. 5 yes 92% 4800 0.006244 769
  76. 6 yes 92% 4800 0.005888 815
  77. 7 yes 55% 12000 0.142196 84
  78. 8 yes 51% 24000 0.277304 87
  79. 9 yes 50% 14401 0.018129 794
  80. 10 yes 86% 14401 0.000132108933
  81. Total 84% 112372 0.483734 232
  82. Perf index = 50 (util) + 15 (thru) = 66/100
  83. ```
  84. ## Version 1.2
  85. #### 使用 `next fit`
  86. * 因为大块的空闲块总是趋向于在后面,所以下一次适配不用从头遍历,可以大幅提高吞吐率
  87. * 引入新的全局变量 `fitted_p` 并且需要在多处维护:初始化,分配,合并
  88. #### 结合 `best fit`
  89. * `next_fit` 会降低内存利用率,而 `best_fit` 会降低吞吐率,但我们可以进行一个折衷,即在 `fitted_p` 后部分首次适配,而在 `fitted_p` 前部分最佳适配
  90. * 而最佳适配仅需找到满足要求的最小值即可
  91. * 然而因为前面的内存碎片太多,测试下来内存利用率的确有所提高,但是吞吐率下降的却更多,所以最后还是选择了 `_next_fit`
  92. #### 得分
  93. ```c
  94. Results for mm malloc:
  95. trace valid util ops secs Kops
  96. 0 yes 91% 5694 0.001803 3158
  97. 1 yes 92% 5848 0.001315 4446
  98. 2 yes 97% 6648 0.003706 1794
  99. 3 yes 97% 5380 0.003602 1494
  100. 4 yes 100% 14400 0.000085169213
  101. 5 yes 91% 4800 0.004207 1141
  102. 6 yes 90% 4800 0.003837 1251
  103. 7 yes 55% 12000 0.057487 209
  104. 8 yes 51% 24000 0.029497 814
  105. 9 yes 50% 14401 0.054370 265
  106. 10 yes 70% 14401 0.000116124684
  107. Total 80% 112372 0.160025 702
  108. Perf index = 48 (util) + 40 (thru) = 88/100
  109. ```
  110. ***
  111. 2022.12.29 ~ 2022.12.30