GC: avoid pathological behaviour; fixes #10040 [backport] (#10052)

This commit is contained in:
Andreas Rumpf 2018-12-20 08:20:32 +01:00 • committed by GitHub
commit cd65e5328d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -17,9 +17,9 @@
const const
CycleIncrease = 2 # is a multiplicative increase CycleIncrease = 2 # is a multiplicative increase
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
ZctThreshold = 500 # we collect garbage if the ZCT's size InitialZctThreshold = 500 # we collect garbage if the ZCT's size
# reaches this threshold # reaches this threshold
# this seems to be a good value # this seems to be a good value
withRealTime = defined(useRealtimeGC) withRealTime = defined(useRealtimeGC)
when withRealTime and not declared(getTicks): when withRealTime and not declared(getTicks):
@ -78,6 +78,7 @@ type
when nimCoroutines: when nimCoroutines:
activeStack: ptr GcStack # current executing coroutine stack. activeStack: ptr GcStack # current executing coroutine stack.
cycleThreshold: int cycleThreshold: int
zctThreshold: int
when useCellIds: when useCellIds:
idGenerator: int idGenerator: int
zct: CellSeq # the zero count table zct: CellSeq # the zero count table
@ -253,6 +254,7 @@ proc initGC() =
when traceGC: when traceGC:
for i in low(CellState)..high(CellState): init(states[i]) for i in low(CellState)..high(CellState): init(states[i])
gch.cycleThreshold = InitialCycleThreshold gch.cycleThreshold = InitialCycleThreshold
gch.zctThreshold = InitialZctThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.cycleCollections = 0
gch.stat.maxThreshold = 0 gch.stat.maxThreshold = 0
@ -771,11 +773,7 @@ proc collectCTBody(gch: var GcHeap) =
c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration) c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration)
proc collectCT(gch: var GcHeap) = proc collectCT(gch: var GcHeap) =
# stackMarkCosts prevents some pathological behaviour: Stack marking if (gch.zct.len >= gch.zctThreshold or (cycleGC and
# becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack.
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
if (gch.zct.len >= stackMarkCosts or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0: gch.recGcLock == 0:
when false: when false:
@ -783,6 +781,7 @@ proc collectCT(gch: var GcHeap) =
cellsetReset(gch.marked) cellsetReset(gch.marked)
markForDebug(gch) markForDebug(gch)
collectCTBody(gch) collectCTBody(gch)
gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
when withRealTime: when withRealTime:
proc toNano(x: int): Nanos {.inline.} = proc toNano(x: int): Nanos {.inline.} =
@ -793,10 +792,11 @@ when withRealTime:
proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) = proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= gch.zctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
strongAdvice: strongAdvice:
collectCTBody(gch) collectCTBody(gch)
gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} = proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
if stackSize >= 0: if stackSize >= 0: