ORC: make the adaptive strategy the default in order to fight memory consumption (#16250)
* ORC: make the adaptive strategy the default in order to fight memory consumption * added missing test case
This commit is contained in:
parent
70d4df0516
commit
af7f7726fe
3 changed files with 52 additions and 7 deletions
|
|
@ -39,7 +39,8 @@ template setColor(c, col) =
|
||||||
c.rc = c.rc and not colorMask or col
|
c.rc = c.rc and not colorMask or col
|
||||||
|
|
||||||
const
|
const
|
||||||
optimizedOrc = not defined(nimOldOrc)
|
optimizedOrc = false # not defined(nimOldOrc)
|
||||||
|
# XXX Still incorrect, see tests/arc/tdestroy_in_loopcond
|
||||||
|
|
||||||
proc nimIncRefCyclic(p: pointer; cyclic: bool) {.compilerRtl, inl.} =
|
proc nimIncRefCyclic(p: pointer; cyclic: bool) {.compilerRtl, inl.} =
|
||||||
let h = head(p)
|
let h = head(p)
|
||||||
|
|
@ -72,6 +73,7 @@ type
|
||||||
jumpStack: CellSeq # Lins' jump stack in order to speed up traversals
|
jumpStack: CellSeq # Lins' jump stack in order to speed up traversals
|
||||||
toFree: CellSeq
|
toFree: CellSeq
|
||||||
freed, touched, edges, rcSum: int
|
freed, touched, edges, rcSum: int
|
||||||
|
keepThreshold: bool
|
||||||
|
|
||||||
proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
|
proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
|
||||||
if desc.traceImpl != nil:
|
if desc.traceImpl != nil:
|
||||||
|
|
@ -309,6 +311,8 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
|
||||||
if j.rcSum == j.edges:
|
if j.rcSum == j.edges:
|
||||||
# short-cut: we know everything is garbage:
|
# short-cut: we know everything is garbage:
|
||||||
colToCollect = colGray
|
colToCollect = colGray
|
||||||
|
# remember the fact that we got so lucky:
|
||||||
|
j.keepThreshold = true
|
||||||
else:
|
else:
|
||||||
for i in countdown(last, lowMark):
|
for i in countdown(last, lowMark):
|
||||||
scan(roots.d[i][0], roots.d[i][1], j)
|
scan(roots.d[i][0], roots.d[i][1], j)
|
||||||
|
|
@ -327,7 +331,7 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
|
||||||
#roots.len = 0
|
#roots.len = 0
|
||||||
|
|
||||||
const
|
const
|
||||||
defaultThreshold = when defined(nimAdaptiveOrc): 128 else: 10_000
|
defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
|
||||||
|
|
||||||
when defined(nimStressOrc):
|
when defined(nimStressOrc):
|
||||||
const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
|
const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
|
||||||
|
|
@ -335,6 +339,7 @@ else:
|
||||||
var rootsThreshold = defaultThreshold
|
var rootsThreshold = defaultThreshold
|
||||||
|
|
||||||
proc partialCollect(lowMark: int) =
|
proc partialCollect(lowMark: int) =
|
||||||
|
when false:
|
||||||
if roots.len < 10 + lowMark: return
|
if roots.len < 10 + lowMark: return
|
||||||
when logOrc:
|
when logOrc:
|
||||||
cfprintf(cstderr, "[partialCollect] begin\n")
|
cfprintf(cstderr, "[partialCollect] begin\n")
|
||||||
|
|
@ -373,9 +378,11 @@ proc collectCycles() =
|
||||||
# of the cycle collector's effectiveness:
|
# of the cycle collector's effectiveness:
|
||||||
# we're effective when we collected 50% or more of the nodes
|
# we're effective when we collected 50% or more of the nodes
|
||||||
# we touched. If we're effective, we can reset the threshold:
|
# we touched. If we're effective, we can reset the threshold:
|
||||||
if j.freed * 2 >= j.touched:
|
if j.keepThreshold and rootsThreshold <= defaultThreshold:
|
||||||
when defined(nimAdaptiveOrc):
|
discard
|
||||||
rootsThreshold = max(rootsThreshold div 2, 16)
|
elif j.freed * 2 >= j.touched:
|
||||||
|
when not defined(nimFixedOrc):
|
||||||
|
rootsThreshold = max(rootsThreshold div 3 * 2, 16)
|
||||||
else:
|
else:
|
||||||
rootsThreshold = defaultThreshold
|
rootsThreshold = defaultThreshold
|
||||||
#cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold)
|
#cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
outputsub: "(allocCount: 4014, deallocCount: 4012)"
|
outputsub: "(allocCount: 4302, deallocCount: 4300)"
|
||||||
cmd: "nim c --gc:orc -d:nimAllocStats $file"
|
cmd: "nim c --gc:orc -d:nimAllocStats $file"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
38
tests/arc/torcbench.nim
Normal file
38
tests/arc/torcbench.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
discard """
|
||||||
|
output: '''true peak memory: true'''
|
||||||
|
cmd: "nim c --gc:orc -d:release $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import lists, strutils, times
|
||||||
|
|
||||||
|
type
|
||||||
|
Base = ref object of RootObj
|
||||||
|
|
||||||
|
Node = ref object of Base
|
||||||
|
parent: DoublyLinkedList[string]
|
||||||
|
le, ri: Node
|
||||||
|
self: Node # in order to create a cycle
|
||||||
|
|
||||||
|
proc buildTree(parent: DoublyLinkedList[string]; depth: int): Node =
|
||||||
|
if depth == 0:
|
||||||
|
result = nil
|
||||||
|
elif depth == 1:
|
||||||
|
result = Node(parent: parent, le: nil, ri: nil, self: nil)
|
||||||
|
when not defined(gcArc):
|
||||||
|
result.self = result
|
||||||
|
else:
|
||||||
|
result = Node(parent: parent, le: buildTree(parent, depth - 1), ri: buildTree(parent, depth - 2), self: nil)
|
||||||
|
result.self = result
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
for i in countup(1, 100):
|
||||||
|
var leakList = initDoublyLinkedList[string]()
|
||||||
|
for j in countup(1, 5000):
|
||||||
|
leakList.append(newString(200))
|
||||||
|
#GC_fullCollect()
|
||||||
|
for i in 0..400:
|
||||||
|
discard buildTree(leakList, 8)
|
||||||
|
|
||||||
|
main()
|
||||||
|
GC_fullCollect()
|
||||||
|
echo getOccupiedMem() < 10 * 1024 * 1024, " peak memory: ", getMaxMem() < 10 * 1024 * 1024
|
||||||
Loading…
Add table
Add a link
Reference in a new issue