mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 13:33:39 +00:00
runtime (gc_blocks.go): use a linked stack to scan marked objects
The blocks GC originally used a fixed-size stack to hold objects to scan. When this stack overflowed, the GC would fully rescan all marked objects. This could cause the GC to degrade to O(n^2) when scanning large linked data structures. Instead of using a fixed-size stack, we now add a pointer field to the start of each object. This pointer field is used to implement an unbounded linked stack. This also consolidates the heap object scanning into one place, which simplifies the process. This comes at the cost of introducing a pointer field to the start of the object, plus the cost of aligning the result. This translates to: - 16 bytes of overhead on x86/arm64 with the conservative collector - 0 bytes of overhead on x86/arm64 with the precise collector (the layout field cost gets aligned up to 16 bytes anyway) - 8 bytes of overhead on other 64-bit systems - 4 bytes of overhead on 32-bit systems - 2 bytes of overhead on AVR
This commit is contained in:
+19
-12
@@ -59,19 +59,19 @@ import "unsafe"
|
||||
|
||||
const preciseHeap = true
|
||||
|
||||
type gcObjectScanner struct {
|
||||
index uintptr
|
||||
size uintptr
|
||||
bitmap uintptr
|
||||
bitmapAddr unsafe.Pointer
|
||||
// parseGCLayout stores the layout information passed to alloc into a gcLayout value.
|
||||
func parseGCLayout(layout unsafe.Pointer) gcLayout {
|
||||
return gcLayout{layout: uintptr(layout)}
|
||||
}
|
||||
|
||||
func newGCObjectScanner(block gcBlock) gcObjectScanner {
|
||||
if gcAsserts && block != block.findHead() {
|
||||
runtimePanic("gc: object scanner must start at head")
|
||||
}
|
||||
scanner := gcObjectScanner{}
|
||||
layout := *(*uintptr)(unsafe.Pointer(block.address()))
|
||||
// gcLayout tracks pointer locations in a heap object.
|
||||
type gcLayout struct {
|
||||
layout uintptr
|
||||
}
|
||||
|
||||
// scanner creates a gcObjectScanner with this layout.
|
||||
func (l gcLayout) scanner() (scanner gcObjectScanner) {
|
||||
layout := l.layout
|
||||
if layout == 0 {
|
||||
// Unknown layout. Assume all words in the object could be pointers.
|
||||
// This layout value below corresponds to a slice of pointers like:
|
||||
@@ -104,7 +104,14 @@ func newGCObjectScanner(block gcBlock) gcObjectScanner {
|
||||
scanner.size = *(*uintptr)(layoutAddr)
|
||||
scanner.bitmapAddr = unsafe.Add(layoutAddr, unsafe.Sizeof(uintptr(0)))
|
||||
}
|
||||
return scanner
|
||||
return
|
||||
}
|
||||
|
||||
type gcObjectScanner struct {
|
||||
index uintptr
|
||||
size uintptr
|
||||
bitmap uintptr
|
||||
bitmapAddr unsafe.Pointer
|
||||
}
|
||||
|
||||
func (scanner *gcObjectScanner) pointerFree() bool {
|
||||
|
||||
Reference in New Issue
Block a user