summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/system/orc.nim59
1 files changed, 45 insertions, 14 deletions
diff --git a/lib/system/orc.nim b/lib/system/orc.nim
index b0497a836..4bd8952b7 100644
--- a/lib/system/orc.nim
+++ b/lib/system/orc.nim
@@ -71,7 +71,7 @@ type
     when useJumpStack:
       jumpStack: CellSeq   # Lins' jump stack in order to speed up traversals
     toFree: CellSeq
-    freed, touched: int
+    freed, touched, edges, rcSum: int
 
 proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
   if desc.traceImpl != nil:
@@ -174,11 +174,14 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
   if s.color != colGray:
     s.setColor colGray
     inc j.touched
+    # keep in mind that refcounts are zero based so add 1 here:
+    inc j.rcSum, (s.rc shr rcShift) + 1
     orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty")
     trace(s, desc, j)
     while j.traceStack.len > 0:
       let (t, desc) = j.traceStack.pop()
       dec t.rc, rcIncrement
+      inc j.edges
       when useJumpStack:
         if (t.rc shr rcShift) >= 0 and (t.rc and jumpStackFlag) == 0:
           t.rc = t.rc or jumpStackFlag
@@ -188,6 +191,8 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
       if t.color != colGray:
         t.setColor colGray
         inc j.touched
+        # we already decremented its refcount so account for that:
+        inc j.rcSum, (t.rc shr rcShift) + 2
         trace(t, desc, j)
 
 proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
@@ -254,8 +259,10 @@ when false:
     cfprintf(cstderr, "%s %p root index: %ld; RC: %ld; color: %ld\n",
       msg, s, s.rootIdx, s.rc shr rcShift, s.color)
 
-proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
+proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) =
   #[
+    was: 'collectWhite'.
+
   proc collectWhite(s: Cell) =
     if s.color == colWhite and not buffered(s):
       s.setColor(colBlack)
@@ -263,7 +270,7 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
         collectWhite(t)
       free(s) # watch out, a bug here!
   ]#
-  if s.color == colWhite and s.rootIdx == 0:
+  if s.color == col and s.rootIdx == 0:
     orcAssert(j.traceStack.len == 0, "collectWhite: trace stack not empty")
 
     s.setColor(colBlack)
@@ -271,12 +278,12 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
     trace(s, desc, j)
     while j.traceStack.len > 0:
       let (t, desc) = j.traceStack.pop()
-      if t.color == colWhite and t.rootIdx == 0:
+      if t.color == col and t.rootIdx == 0:
         j.toFree.add(t, desc)
         t.setColor(colBlack)
         trace(t, desc, j)
 
-proc collectCyclesBacon(j: var GcEnv) =
+proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
   # pretty direct translation from
   # https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf
   # Fig. 2. Synchronous Cycle Collection
@@ -290,20 +297,27 @@ proc collectCyclesBacon(j: var GcEnv) =
       s.buffered = false
       collectWhite(s)
   ]#
+  let last = roots.len - 1
   when logOrc:
-    for i in 0 ..< roots.len:
+    for i in countdown(last, lowMark):
       writeCell("root", roots.d[i][0], roots.d[i][1])
 
-  for i in 0 ..< roots.len:
+  for i in countdown(last, lowMark):
     markGray(roots.d[i][0], roots.d[i][1], j)
-  for i in 0 ..< roots.len:
-    scan(roots.d[i][0], roots.d[i][1], j)
+
+  var colToCollect = colWhite
+  if j.rcSum == j.edges:
+    # short-cut: we know everything is garbage:
+    colToCollect = colGray
+  else:
+    for i in countdown(last, lowMark):
+      scan(roots.d[i][0], roots.d[i][1], j)
 
   init j.toFree
   for i in 0 ..< roots.len:
     let s = roots.d[i][0]
     s.rootIdx = 0
-    collectWhite(s, roots.d[i][1], j)
+    collectColor(s, roots.d[i][1], colToCollect, j)
 
   for i in 0 ..< j.toFree.len:
     free(j.toFree.d[i][0], j.toFree.d[i][1])
@@ -320,6 +334,19 @@ when defined(nimStressOrc):
 else:
   var rootsThreshold = defaultThreshold
 
+proc partialCollect(lowMark: int) =
+  if roots.len < 10 + lowMark: return
+  when logOrc:
+    cfprintf(cstderr, "[partialCollect] begin\n")
+  var j: GcEnv
+  init j.traceStack
+  collectCyclesBacon(j, lowMark)
+  when logOrc:
+    cfprintf(cstderr, "[partialCollect] end; freed %ld touched: %ld work: %ld\n", j.freed, j.touched,
+      roots.len - lowMark)
+  roots.len = lowMark
+  deinit j.traceStack
+
 proc collectCycles() =
   ## Collect cycles.
   when logOrc:
@@ -329,14 +356,14 @@ proc collectCycles() =
   init j.traceStack
   when useJumpStack:
     init j.jumpStack
-    collectCyclesBacon(j)
+    collectCyclesBacon(j, 0)
     while j.jumpStack.len > 0:
       let (t, desc) = j.jumpStack.pop
       # not in jump stack anymore!
       t.rc = t.rc and not jumpStackFlag
     deinit j.jumpStack
   else:
-    collectCyclesBacon(j)
+    collectCyclesBacon(j, 0)
 
   deinit j.traceStack
   deinit roots
@@ -355,8 +382,8 @@ proc collectCycles() =
     elif rootsThreshold < high(int) div 4:
       rootsThreshold = rootsThreshold * 3 div 2
   when logOrc:
-    cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld\n", j.freed, rootsThreshold, j.touched,
-      getOccupiedMem())
+    cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld rcSum: %ld edges: %ld\n", j.freed, rootsThreshold, j.touched,
+      getOccupiedMem(), j.rcSum, j.edges)
 
 proc registerCycle(s: Cell; desc: PNimTypeV2) =
   s.rootIdx = roots.len+1
@@ -384,6 +411,10 @@ proc GC_disableOrc*() =
   when not defined(nimStressOrc):
     rootsThreshold = high(int)
 
+proc GC_prepareOrc*(): int {.inline.} = roots.len
+
+proc GC_partialCollect*(limit: int) =
+  partialCollect(limit)
 
 proc GC_fullCollect* =
   ## Forces a full garbage collection pass. With ``--gc:orc`` triggers the cycle