Skip to content

Commit 82b5748

Browse files
committed
docs: better immix mark doc
1 parent a94bee7 commit 82b5748

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

book/src/references/bdw.png

970 KB
Loading

book/src/references/immix.md

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,73 @@ pub type VtableFunc = fn(*mut u8, &Collector, VisitFunc, VisitFunc, VisitFunc);
213213

214214
对于Trait Object,我们需要遍历他指向实际值的指针
215215

216+
下方是一个immix heap的示意图,其中`*`表示该对象会在mark阶段中被标记
217+
218+
```mermaid
219+
graph LR;
220+
subgraph Roots
221+
Root1
222+
Root2
223+
Root3
224+
Root4
225+
end
226+
subgraph HO[Heap]
227+
AtomicObject1[AtomicObject1*]
228+
AtomicObject2[AtomicObject2*]
229+
AtomicObject3
230+
PointerObject1[PointerObject1*]
231+
PointerObject2[PointerObject2*]
232+
ComplexObject1
233+
end
234+
subgraph ComplexObject1[ComplexObject1*]
235+
VT1[VTable]
236+
PF1[PointerField]
237+
AF1[AtomicField]
238+
ComplexField
239+
end
240+
subgraph ComplexField
241+
VT2[VTable]
242+
AF2[AtomicField]
243+
PF2[PointerField]
244+
end
245+
PF1 --> PointerObject1
246+
PF2 --> AtomicObject2
247+
PointerObject1 --> AtomicObject1
248+
PointerObject2 --> ComplexObject1
249+
Root1 --> PointerObject1
250+
Root2 --> PointerObject2
251+
Root3 --> PointerObject1
252+
253+
```
254+
255+
对于`ComplexObject1`,他的vtable函数逻辑如下:
256+
```rust,ignore
257+
fn vtable_complex_obj1(&self, mark_ptr: VisitFunc, mark_complex: VisitFunc, mark_trait: VisitFunc){
258+
mark_ptr(self.PointerField)
259+
mark_complex(self.ComplexField)
260+
}
261+
```
262+
而对于`ComplexField`,他的vtable函数逻辑如下:
263+
```rust,ignore
264+
fn vtable_complex_field(&self, mark_ptr: VisitFunc, mark_complex: VisitFunc, mark_trait: VisitFunc){
265+
mark_ptr(self.PointerField)
266+
}
267+
```
268+
269+
实际上,`mark_complex``mark_trait`逻辑都十分简单:`mark_complex`只是调用对象的vtable函数,而
270+
`mark_trait`只是对实际指针调用`mark_ptr`,真正的标记和驱逐逻辑都在`mark_ptr`中实现。
271+
272+
```admonish tip
273+
标记过程开始的时候,gc会load所有root指向的值,对他们调用`mark_ptr`,如果该值为gc堆中的对象,
274+
则会将该对象标记,并且再次load它指向的对象,若该对象非AtomicObject类型则加入到mark queue中。
275+
276+
在mark queue中的对象,会被逐个取出,根据他们的类型对他们调用`mark_ptr`、`mark_complex`或者`mark_trait`,直到
277+
mark queue为空,则标记过程结束。
278+
279+
尽管这的确可以看作是一个递归的过程,但是此过程一定不能使用递归的方式实现,因为递归的方式在复杂程序中可能会导致栈溢出。
280+
```
281+
282+
216283
## Sweep
217284

218285
Sweep阶段的主要工作是:
@@ -296,11 +363,19 @@ fn add_sub_ndoe(root: *mut Node) {
296363
## 性能
297364

298365
我们与bdwgc进行了很多比较,数据证明在大多数情况下,我们的分配算法略慢于bdwgc,与malloc速度相当,但是在回收的时候,我们的回收速度要快于bdwgc。
299-
对于一些复杂的测试,在触发回收的策略相同的情况下,我们的单线程总执行时间略慢于bdwgc(
300-
测试采用shadow stack算法,维护shadow stack的额外开销较高),但是在多线程情况下,我们的总执行时间明显快于bdwgc(约3倍)。
366+
对于一些复杂的测试,在触发回收的策略相同的情况下,我们的单线程总执行时间略慢于bdwgc,但是在多线程情况下,我们的总执行时间明显快于bdwgc,
367+
整体来说机器并行能力越强、测试时使用内存越多immix性能优势越大。
368+
369+
使用github action进行的基准测试结果可以在[这里](https://chronostasys.github.io/bdwgcvsimmix-bench/report/)查看,由于github action使用的机器
370+
只有两个核心,所以测试线程数量为2,在此结果中,可以看到immix整体性能略差于bdwgc,但是差距小于单线程情况。
371+
372+
你可以从[这里](https://github.com/Chronostasys/bdwgcvsimmix-bench)下载测试代码,在你的机器上运行并进行比较。这里我提供一组笔者机器上的测试数据截图
373+
374+
![](immix.png)
375+
376+
![](bdw.png)
301377

302-
如果你对性能感兴趣,可以在immix目录下使用`cargo bench`命令运行gc的基准测试。bdwgc测试代码基于复杂性考虑没有放在本仓库中,未来可能会单独在一个
303-
仓库中放出。
378+
测试环境为MacBook Pro (16-inch, 2021) Apple M1 Pro 16 GB,可以看出immix在此环境中已经具有近4倍的性能优势。
304379

305380
immix作为天生并发的gc,并发情况下几乎能完全避免锁竞争的出现,因此在多线程情况下的性能优势是非常明显的。并且其分配算法很好的维护了空间局部性,理论上
306381
能带来更好的mutator性能。

book/src/references/immix.png

990 KB
Loading

0 commit comments

Comments
 (0)