code cleanup for mark&sweep GC
This commit is contained in:
parent
ab6f793408
commit
4ee9816a78
7 changed files with 43 additions and 88 deletions
|
|
@ -21,7 +21,7 @@ proc registerGcRoot(p: BProc, v: PSym) =
|
||||||
# that it works out of the box for thread local storage then :-)
|
# that it works out of the box for thread local storage then :-)
|
||||||
let prc = genTraverseProcForGlobal(p.module, v)
|
let prc = genTraverseProcForGlobal(p.module, v)
|
||||||
linefmt(p.module.initProc, cpsStmts,
|
linefmt(p.module.initProc, cpsStmts,
|
||||||
"#nimRegisterGlobalMarker((void*)$1);$n", prc)
|
"#nimRegisterGlobalMarker($1);$n", prc)
|
||||||
|
|
||||||
proc genVarTuple(p: BProc, n: PNode) =
|
proc genVarTuple(p: BProc, n: PNode) =
|
||||||
var tup, field: TLoc
|
var tup, field: TLoc
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,8 @@ Advanced options:
|
||||||
--skipUserCfg do not read the user's configuration file
|
--skipUserCfg do not read the user's configuration file
|
||||||
--skipParentCfg do not read the parent dirs' configuration files
|
--skipParentCfg do not read the parent dirs' configuration files
|
||||||
--skipProjCfg do not read the project's configuration file
|
--skipProjCfg do not read the project's configuration file
|
||||||
--gc:refc|v2|boehm|none use Nimrod's native GC|V2|Boehm GC|no GC
|
--gc:refc|v2|markAndSweep|boehm|none
|
||||||
|
select the GC to use; default is 'refc'
|
||||||
--index:on|off turn index file generation on|off
|
--index:on|off turn index file generation on|off
|
||||||
--putenv:key=value set an environment variable
|
--putenv:key=value set an environment variable
|
||||||
--babelPath:PATH add a path for Babel support
|
--babelPath:PATH add a path for Babel support
|
||||||
|
|
|
||||||
|
|
@ -68,38 +68,6 @@ proc deinit(s: var TCellSeq) =
|
||||||
s.len = 0
|
s.len = 0
|
||||||
s.cap = 0
|
s.cap = 0
|
||||||
|
|
||||||
# ------------------- cyclic cell temporary data structure --------------------
|
|
||||||
|
|
||||||
type
|
|
||||||
TCycleCell = object
|
|
||||||
cell: PCell
|
|
||||||
oldRefcount, newRefcount: TRefCount
|
|
||||||
PCycleCellArray = ptr array[0..100_000_000, TCycleCell]
|
|
||||||
TCycleCellSeq {.final, pure.} = object
|
|
||||||
len, cap: int
|
|
||||||
d: PCycleCellArray
|
|
||||||
|
|
||||||
proc reserveSlot(s: var TCycleCellSeq): int =
|
|
||||||
if s.len >= s.cap:
|
|
||||||
s.cap = s.cap * 3 div 2
|
|
||||||
var d = cast[PCycleCellArray](Alloc(s.cap * sizeof(TCycleCell)))
|
|
||||||
copyMem(d, s.d, s.len * sizeof(TCycleCell))
|
|
||||||
Dealloc(s.d)
|
|
||||||
s.d = d
|
|
||||||
result = s.len
|
|
||||||
inc(s.len)
|
|
||||||
|
|
||||||
proc init(s: var TCycleCellSeq, cap: int = 1024) =
|
|
||||||
s.len = 0
|
|
||||||
s.cap = cap
|
|
||||||
s.d = cast[PCycleCellArray](Alloc(cap * sizeof(TCycleCell)))
|
|
||||||
|
|
||||||
proc deinit(s: var TCycleCellSeq) =
|
|
||||||
Dealloc(s.d)
|
|
||||||
s.d = nil
|
|
||||||
s.len = 0
|
|
||||||
s.cap = 0
|
|
||||||
|
|
||||||
# ------------------- cell set handling ---------------------------------------
|
# ------------------- cell set handling ---------------------------------------
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -230,7 +198,7 @@ iterator elements(t: TCellSet): PCell {.inline.} =
|
||||||
inc(i)
|
inc(i)
|
||||||
r = r.next
|
r = r.next
|
||||||
|
|
||||||
iterator elementsWithout(t, s: TCellSet): PCell {.inline.} =
|
iterator elementsExcept(t, s: TCellSet): PCell {.inline.} =
|
||||||
var r = t.head
|
var r = t.head
|
||||||
while r != nil:
|
while r != nil:
|
||||||
let ss = CellSetGet(s, r.key)
|
let ss = CellSetGet(s, r.key)
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@
|
||||||
{.push profiler:off.}
|
{.push profiler:off.}
|
||||||
|
|
||||||
const
|
const
|
||||||
CycleIncrease = 2 # is a multiplicative increase
|
InitialThreshold = 4*1024*1024 # X MB because marking&sweeping is slow
|
||||||
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
|
|
||||||
|
template mulThreshold(x): expr {.immediate.} = x * 2
|
||||||
|
|
||||||
when defined(memProfiler):
|
when defined(memProfiler):
|
||||||
proc nimProfile(requestedSize: int)
|
proc nimProfile(requestedSize: int)
|
||||||
|
|
@ -31,12 +32,10 @@ type
|
||||||
TGlobalMarkerProc = proc () {.nimcall.}
|
TGlobalMarkerProc = proc () {.nimcall.}
|
||||||
|
|
||||||
TGcStat = object
|
TGcStat = object
|
||||||
stackScans: int # number of performed stack scans (for statistics)
|
|
||||||
collections: int # number of performed full collections
|
collections: int # number of performed full collections
|
||||||
maxThreshold: int # max threshold that has been set
|
maxThreshold: int # max threshold that has been set
|
||||||
maxStackSize: int # max stack size
|
maxStackSize: int # max stack size
|
||||||
maxStackCells: int # max stack cells in ``decStack``
|
freedObjects: int # max entries in cycle table
|
||||||
cycleTableSize: int # max entries in cycle table
|
|
||||||
|
|
||||||
TGcHeap = object # this contains the zero count and
|
TGcHeap = object # this contains the zero count and
|
||||||
# non-zero count table
|
# non-zero count table
|
||||||
|
|
@ -87,33 +86,24 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.inline.} =
|
||||||
dest[] = src
|
dest[] = src
|
||||||
|
|
||||||
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||||
result = int(usrToCell(p).refcount)
|
result = 0
|
||||||
|
|
||||||
var
|
var
|
||||||
globalMarkersLen: int
|
globalMarkersLen: int
|
||||||
globalMarkers: array[0.. 10_000, TGlobalMarkerProc]
|
globalMarkers: array[0.. 7_000, TGlobalMarkerProc]
|
||||||
|
|
||||||
proc nimRegisterGlobalMarker(markerProc: pointer) {.compilerProc.} =
|
proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} =
|
||||||
globalMarkers[globalMarkersLen] = cast[TGlobalMarkerProc](markerProc)
|
if globalMarkersLen <= high(globalMarkers):
|
||||||
|
globalMarkers[globalMarkersLen] = markerProc
|
||||||
inc globalMarkersLen
|
inc globalMarkersLen
|
||||||
|
else:
|
||||||
|
echo "[GC] cannot register global variable; too many global variables"
|
||||||
|
quit 1
|
||||||
|
|
||||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||||
|
|
||||||
proc writeCell(msg: CString, c: PCell) =
|
|
||||||
var kind = -1
|
|
||||||
if c.typ != nil: kind = ord(c.typ.kind)
|
|
||||||
when leakDetector:
|
|
||||||
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n",
|
|
||||||
msg, c, kind, c.refcount, c.filename, c.line)
|
|
||||||
else:
|
|
||||||
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n",
|
|
||||||
msg, c, kind, c.refcount)
|
|
||||||
|
|
||||||
template gcTrace(cell, state: expr): stmt {.immediate.} =
|
|
||||||
when traceGC: traceCell(cell, state)
|
|
||||||
|
|
||||||
# forward declarations:
|
# forward declarations:
|
||||||
proc collectCT(gch: var TGcHeap)
|
proc collectCT(gch: var TGcHeap)
|
||||||
proc IsOnStack*(p: pointer): bool {.noinline.}
|
proc IsOnStack*(p: pointer): bool {.noinline.}
|
||||||
|
|
@ -133,20 +123,18 @@ proc prepareDealloc(cell: PCell) =
|
||||||
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
|
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||||
dec(gch.recGcLock)
|
dec(gch.recGcLock)
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerProc, inline.} = inc(usrToCell(p).refCount)
|
proc nimGCref(p: pointer) {.compilerProc, inline.} =
|
||||||
proc nimGCunref(p: pointer) {.compilerProc, inline.} = dec(usrToCell(p).refCount)
|
# we keep it from being collected by pretending it's not even allocated:
|
||||||
|
excl(gch.allocated, usrToCell(p))
|
||||||
|
proc nimGCunref(p: pointer) {.compilerProc, inline.} =
|
||||||
|
incl(gch.allocated, usrToCell(p))
|
||||||
|
|
||||||
proc initGC() =
|
proc initGC() =
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
when traceGC:
|
gch.cycleThreshold = InitialThreshold
|
||||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
|
||||||
gch.cycleThreshold = InitialCycleThreshold
|
|
||||||
gch.stat.stackScans = 0
|
|
||||||
gch.stat.collections = 0
|
gch.stat.collections = 0
|
||||||
gch.stat.maxThreshold = 0
|
gch.stat.maxThreshold = 0
|
||||||
gch.stat.maxStackSize = 0
|
gch.stat.maxStackSize = 0
|
||||||
gch.stat.maxStackCells = 0
|
|
||||||
# init the rt
|
|
||||||
init(gch.tempStack)
|
init(gch.tempStack)
|
||||||
Init(gch.allocated)
|
Init(gch.allocated)
|
||||||
init(gch.marked)
|
init(gch.marked)
|
||||||
|
|
@ -290,30 +278,27 @@ proc doOperation(p: pointer, op: TWalkOp) =
|
||||||
gcAssert(c != nil, "doOperation: 1")
|
gcAssert(c != nil, "doOperation: 1")
|
||||||
case op
|
case op
|
||||||
of waMarkGlobal:
|
of waMarkGlobal:
|
||||||
|
when hasThreadSupport:
|
||||||
|
# could point to a cell which we don't own and don't want to touch/trace
|
||||||
if isAllocatedPtr(gch.region, c):
|
if isAllocatedPtr(gch.region, c):
|
||||||
mark(gch, c)
|
mark(gch, c)
|
||||||
|
else:
|
||||||
|
mark(gch, c)
|
||||||
of waMarkPrecise: add(gch.tempStack, c)
|
of waMarkPrecise: add(gch.tempStack, c)
|
||||||
|
|
||||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||||
doOperation(d, TWalkOp(op))
|
doOperation(d, TWalkOp(op))
|
||||||
|
|
||||||
proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
|
proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
|
||||||
|
inc gch.stat.freedObjects
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
gcTrace(c, csCycFreed)
|
|
||||||
when logGC: writeCell("cycle collector dealloc cell", c)
|
|
||||||
when reallyDealloc: rawDealloc(gch.region, c)
|
when reallyDealloc: rawDealloc(gch.region, c)
|
||||||
else:
|
else:
|
||||||
gcAssert(c.typ != nil, "freeCyclicCell")
|
gcAssert(c.typ != nil, "freeCyclicCell")
|
||||||
zeroMem(c, sizeof(TCell))
|
zeroMem(c, sizeof(TCell))
|
||||||
|
|
||||||
proc sweep(gch: var TGcHeap) =
|
proc sweep(gch: var TGcHeap) =
|
||||||
when true:
|
for c in gch.allocated.elementsExcept(gch.marked):
|
||||||
for c in gch.allocated.elementsWithout(gch.marked):
|
|
||||||
gch.allocated.excl(c)
|
|
||||||
freeCyclicCell(gch, c)
|
|
||||||
else:
|
|
||||||
for c in gch.allocated.elements():
|
|
||||||
if not gch.marked.contains(c):
|
|
||||||
gch.allocated.excl(c)
|
gch.allocated.excl(c)
|
||||||
freeCyclicCell(gch, c)
|
freeCyclicCell(gch, c)
|
||||||
|
|
||||||
|
|
@ -465,12 +450,10 @@ proc collectCTBody(gch: var TGcHeap) =
|
||||||
markGlobals(gch)
|
markGlobals(gch)
|
||||||
sweep(gch)
|
sweep(gch)
|
||||||
|
|
||||||
inc(gch.stat.stackScans)
|
|
||||||
inc(gch.stat.collections)
|
inc(gch.stat.collections)
|
||||||
deinit(gch.marked)
|
deinit(gch.marked)
|
||||||
init(gch.marked)
|
init(gch.marked)
|
||||||
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
gch.cycleThreshold = max(InitialThreshold, getOccupiedMem().mulThreshold)
|
||||||
cycleIncrease)
|
|
||||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||||
sysAssert(allocInv(gch.region), "collectCT: end")
|
sysAssert(allocInv(gch.region), "collectCT: end")
|
||||||
|
|
||||||
|
|
@ -494,7 +477,7 @@ when not defined(useNimRtl):
|
||||||
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
||||||
|
|
||||||
proc GC_enableMarkAndSweep() =
|
proc GC_enableMarkAndSweep() =
|
||||||
gch.cycleThreshold = InitialCycleThreshold
|
gch.cycleThreshold = InitialThreshold
|
||||||
|
|
||||||
proc GC_disableMarkAndSweep() =
|
proc GC_disableMarkAndSweep() =
|
||||||
gch.cycleThreshold = high(gch.cycleThreshold)-1
|
gch.cycleThreshold = high(gch.cycleThreshold)-1
|
||||||
|
|
@ -512,13 +495,10 @@ when not defined(useNimRtl):
|
||||||
GC_disable()
|
GC_disable()
|
||||||
result = "[GC] total memory: " & $getTotalMem() & "\n" &
|
result = "[GC] total memory: " & $getTotalMem() & "\n" &
|
||||||
"[GC] occupied memory: " & $getOccupiedMem() & "\n" &
|
"[GC] occupied memory: " & $getOccupiedMem() & "\n" &
|
||||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
|
||||||
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
|
||||||
"[GC] collections: " & $gch.stat.collections & "\n" &
|
"[GC] collections: " & $gch.stat.collections & "\n" &
|
||||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
"[GC] freed objects: " & $gch.stat.freedObjects & "\n" &
|
||||||
"[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
"[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||||
when traceGC: writeLeakage()
|
|
||||||
GC_enable()
|
GC_enable()
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ type
|
||||||
proc MakeObj(): TTestObj =
|
proc MakeObj(): TTestObj =
|
||||||
result.x = "Hello"
|
result.x = "Hello"
|
||||||
|
|
||||||
for i in 1 .. 100_000_000:
|
for i in 1 .. 1_000_000:
|
||||||
|
when defined(gcMarkAndSweep):
|
||||||
|
GC_fullcollect()
|
||||||
var obj = MakeObj()
|
var obj = MakeObj()
|
||||||
if getOccupiedMem() > 300_000: quit("still a leak!")
|
if getOccupiedMem() > 300_000: quit("still a leak!")
|
||||||
# echo GC_getstatistics()
|
# echo GC_getstatistics()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ proc MakeObj(): TTestObj =
|
||||||
result.s = @[1,2,3]
|
result.s = @[1,2,3]
|
||||||
|
|
||||||
proc inProc() =
|
proc inProc() =
|
||||||
for i in 1 .. 100_000_000:
|
for i in 1 .. 1_000_000:
|
||||||
|
when defined(gcMarkAndSweep):
|
||||||
|
GC_fullcollect()
|
||||||
var obj: TTestObj
|
var obj: TTestObj
|
||||||
obj = MakeObj()
|
obj = MakeObj()
|
||||||
if getOccupiedMem() > 300_000: quit("still a leak!")
|
if getOccupiedMem() > 300_000: quit("still a leak!")
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ proc main =
|
||||||
for i in 0 .. s.high:
|
for i in 0 .. s.high:
|
||||||
s[i] = register(create())
|
s[i] = register(create())
|
||||||
# test that we have at least 80% unreachable weak objects by now:
|
# test that we have at least 80% unreachable weak objects by now:
|
||||||
|
when defined(gcMarkAndSweep):
|
||||||
|
GC_fullcollect()
|
||||||
var unreachable = 0
|
var unreachable = 0
|
||||||
for i in 0 .. s.high:
|
for i in 0 .. s.high:
|
||||||
if access(s[i]) == nil: inc unreachable
|
if access(s[i]) == nil: inc unreachable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue