Skip to content

Commit f98fba8

Browse files
Update final_doc.md
1 parent 516519e commit f98fba8

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

slides/final_doc.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,199 @@
2121

2222

2323

24+
#### 内存分配概述
25+
26+
内存分配器需要实现的功能:
27+
28+
- init (addr, size):初始化内存分配器,向内存分配器提供一段[addr,addr+size)的内存空间;
29+
30+
- add_memory (addr, size):向内存分配器额外增加一段[addr,addr+size)的内存空间;
31+
32+
- alloc (size, align):申请一段空间,大小为size,地址对齐要求为align(为2的幂,一般为8字节),返回分配空间的起始地址addr;
33+
34+
- dealloc (addr, size, align):释放一段先前申请的空间,起始地址为addr,大小为size,地址对齐要求为align。
35+
36+
评价内存分配算法的指标:
37+
38+
- 性能:通常要求内存分配器单次O(1);不仅取决于性能分配器的效率本身,还有分配内存的连续性等各种因素;
39+
40+
- 空间利用率:尽可能高,内部碎块与外部碎块尽可能少;
41+
42+
- 线程安全等。
43+
44+
45+
46+
#### 算法介绍
47+
48+
##### (1)basic allocator
49+
50+
将全局所有的空闲块用一个链表来维护
51+
52+
- Alloc时,查找一个合适的空闲块,切割为合适大小后分配
53+
54+
- Free时,将其与前后的空闲块合并后插入回链表中
55+
56+
- 根据查找空闲块的策略不同,分为first fit、best fit、worst fit三种
57+
58+
- first fit:选取第一个大小足够的内存块
59+
- best fit:选取大小足够的内存块中最小的
60+
61+
- worst fit:选取大小足够的内存块中最大的
62+
63+
优点:实现相对容易;消除了内碎块
64+
65+
缺点:可能存在外碎块;查找空闲块时最坏需要遍历整个链表,效率低
66+
67+
上述代码约600行,于第8周完成。
68+
69+
70+
71+
##### (2)TLSF
72+
73+
用两级链表来维护大小在一定范围内的内存块
74+
75+
O(1)的malloc与dealloc;
76+
77+
每次分配的额外空间开销仅为8字节;
78+
79+
内存碎片较少;
80+
81+
支持动态添加和删除内存池;
82+
83+
详细介绍见文档 `tlsf_note.md`
84+
85+
优点:单次操作复杂度严格O(1);内碎块相对Buddy和slab更少
86+
87+
- 取决于二级链表的大小,如取5位则内碎块不超过1/64
88+
89+
缺点:
90+
91+
- 每次操作时,拆分和合并内存块的开销相对较大;
92+
93+
- 多次申请空间很可能不连续;
94+
95+
- 最小分配单位为16字节,分配小内存块时冗余较大
96+
97+
分别使用Rust语言实现和接入C语言的既有实现,Rust代码量约1000行,于第11周完成。
98+
99+
100+
101+
##### (3)mimalloc
102+
103+
原始算法是保证线程安全的:即可以支持多线程同时申请/释放,无需上锁(Mutex),仅需原子操作(Atomic)
104+
105+
但算法相对较复杂(原版C代码~3500行),目前实现的Rust版本为单线程的简化版
106+
107+
在mimalloc内存分配器中,内存维护单元分为:堆(Heap)、段(Segment)、页(Page)、块(Block)
108+
109+
每个Page中的块大小都是相同的
110+
111+
Segment以4MB对齐,承载各种Page
112+
113+
每个线程用一个Heap作为mimalloc的控制结构,核心为维护不同block大小的Page链表
114+
115+
详细介绍见文档 `mimalloc_note.md`
116+
117+
优点:单次操作O(1);连续分配时内存地址大致连续;速度较现在通用的内存分配器快约7%~14%;内碎块相对较小;
118+
119+
缺点:
120+
121+
- 以段(4MB)和页(64KB)为单位维护内存,在分配请求不规则时可能有较大冗余;
122+
123+
- 算法较为复杂。
124+
125+
当前的Rust语言单线程版本,代码量约1000行,于第15周完成。
126+
127+
128+
129+
#### APP测试
130+
131+
主要用于测试内存分配的两个app为 `apps/memtest``apps/c/memtest`
132+
133+
134+
24135
#### 用户态测试
25136

137+
`crates/allocator/tests` 中搭建了专用于用户态测试的global_allocator和测试框架
138+
139+
- global_allocator接入了上述各种内存分配器,以及rust自带的System分配器(linux中,使用的是__libc_malloc)
140+
141+
- 通过init_heap函数接入一个全局static的大数组(512MB)用于内存分配
142+
143+
- 通过各种分配器对应的init函数来动态切换使用的分配器
144+
145+
- 实现了各种测试用例的接口以及各种分配器的测试入口,集成为一个test
146+
147+
- 多线程支持:在每个内存分配器外面套一层mutex
148+
149+
新建了`crates/allocator_test`,内部集成了各种测试用例,可以在运行用户态测试时调用
150+
151+
各个测试用例的介绍:
152+
153+
##### (1) Basic
154+
155+
- Rust语言
156+
157+
- 与apps/memtest类似
158+
159+
- 主要为大量Vec和btreemap测试
160+
161+
##### (2) Align_test
162+
163+
- Rust语言,自行编写
164+
165+
- 测试align为非8字节的情况
166+
167+
- 仅有System、TLSF(C和Rust)、mimalloc支持该功能
168+
169+
##### (3) Multi_thread_test
170+
171+
- Rust语言,自行编写
172+
173+
- 测试多线程情况下的内存分配效率
174+
175+
##### (4) Mitest
176+
177+
- C语言
178+
179+
- Mimalloc仓库中自带的测试用例
180+
181+
- 仓库地址:https://github.com/microsoft/mimalloc
182+
183+
- 主要测试算法的正确性
184+
185+
##### (5) Glibc_bench
186+
187+
- C语言
188+
189+
- 仓库地址:https://github.com/daanx/mimalloc-bench
190+
191+
- 位于`bench/glibc-bench`
192+
193+
- 一系列比较复杂的内存操作,测试各种算法的性能
194+
195+
##### (6) Malloc_large
196+
197+
- C语言
198+
199+
- 仓库地址:https://github.com/daanx/mimalloc-bench
200+
201+
- 位于`bench/malloc-large`
202+
203+
- 用于测试对大内存(2~15MB)的申请与释放
204+
205+
##### (7) Multi_thread_c_test
206+
207+
- C语言,自行编写
208+
209+
- 功能大致类似于multi_thread_test
210+
211+
- 测试C语言下的多线程内存分配
212+
213+
214+
215+
#### 运行用户态测试
216+
26217
```
27218
cargo test -p allocator --release -- --nocapture
28219
```

0 commit comments

Comments
 (0)