summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2020-12-05 23:15:17 +0100
committerGitHub <noreply@github.com>2020-12-05 23:15:17 +0100
commitaf7f7726fe727b68238533f690fb7cd98552e4e4 (patch)
treee0621ef74aa7cbdb554637185d45149f3d1727b2
parent70d4df051648771269251cca8b4a6aa32ae636d0 (diff)
downloadNim-af7f7726fe727b68238533f690fb7cd98552e4e4.tar.gz
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
-rw-r--r--lib/system/orc.nim19
-rw-r--r--tests/arc/tasyncleak.nim2
-rw-r--r--tests/arc/torcbench.nim38
3 files changed, 52 insertions, 7 deletions
diff --git a/lib/system/orc.nim b/lib/system/orc.nim
index 4bd8952b7..803f3d410 100644
--- a/lib/system/orc.nim
+++ b/lib/system/orc.nim
@@ -39,7 +39,8 @@ template setColor(c, col) =
     c.rc = c.rc and not colorMask or col
 
 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.} =
   let h = head(p)
@@ -72,6 +73,7 @@ type
       jumpStack: CellSeq   # Lins' jump stack in order to speed up traversals
     toFree: CellSeq
     freed, touched, edges, rcSum: int
+    keepThreshold: bool
 
 proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
   if desc.traceImpl != nil:
@@ -309,6 +311,8 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
   if j.rcSum == j.edges:
     # short-cut: we know everything is garbage:
     colToCollect = colGray
+    # remember the fact that we got so lucky:
+    j.keepThreshold = true
   else:
     for i in countdown(last, lowMark):
       scan(roots.d[i][0], roots.d[i][1], j)
@@ -327,7 +331,7 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
   #roots.len = 0
 
 const
-  defaultThreshold = when defined(nimAdaptiveOrc): 128 else: 10_000
+  defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
 
 when defined(nimStressOrc):
   const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
@@ -335,7 +339,8 @@ else:
   var rootsThreshold = defaultThreshold
 
 proc partialCollect(lowMark: int) =
-  if roots.len < 10 + lowMark: return
+  when false:
+    if roots.len < 10 + lowMark: return
   when logOrc:
     cfprintf(cstderr, "[partialCollect] begin\n")
   var j: GcEnv
@@ -373,9 +378,11 @@ proc collectCycles() =
     # of the cycle collector's effectiveness:
     # we're effective when we collected 50% or more of the nodes
     # we touched. If we're effective, we can reset the threshold:
-    if j.freed * 2 >= j.touched:
-      when defined(nimAdaptiveOrc):
-        rootsThreshold = max(rootsThreshold div 2, 16)
+    if j.keepThreshold and rootsThreshold <= defaultThreshold:
+      discard
+    elif j.freed * 2 >= j.touched:
+      when not defined(nimFixedOrc):
+        rootsThreshold = max(rootsThreshold div 3 * 2, 16)
       else:
         rootsThreshold = defaultThreshold
       #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold)
diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim
index 417b67edb..eb0c45213 100644
--- a/tests/arc/tasyncleak.nim
+++ b/tests/arc/tasyncleak.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: "(allocCount: 4014, deallocCount: 4012)"
+  outputsub: "(allocCount: 4302, deallocCount: 4300)"
   cmd: "nim c --gc:orc -d:nimAllocStats $file"
 """
 
diff --git a/tests/arc/torcbench.nim b/tests/arc/torcbench.nim
new file mode 100644
index 000000000..4c9e65fee
--- /dev/null
+++ b/tests/arc/torcbench.nim
@@ -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