cellsets.nim 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. #[
  10. Efficient set of pointers for the GC (and repr)
  11. -----------------------------------------------
  12. The GC depends on an extremely efficient datastructure for storing a
  13. set of pointers - this is called a `CellSet` in the source code.
  14. Inserting, deleting and searching are done in constant time. However,
  15. modifying a `CellSet` during traversal leads to undefined behaviour.
  16. All operations on a CellSet have to perform efficiently. Because a Cellset can
  17. become huge a hash table alone is not suitable for this.
  18. We use a mixture of bitset and hash table for this. The hash table maps *pages*
  19. to a page descriptor. The page descriptor contains a bit for any possible cell
  20. address within this page. So including a cell is done as follows:
  21. - Find the page descriptor for the page the cell belongs to.
  22. - Set the appropriate bit in the page descriptor indicating that the
  23. cell points to the start of a memory block.
  24. Removing a cell is analogous - the bit has to be set to zero.
  25. Single page descriptors are never deleted from the hash table. This is not
  26. needed as the data structures needs to be rebuilt periodically anyway.
  27. Complete traversal is done in this way::
  28. for each page descriptor d:
  29. for each bit in d:
  30. if bit == 1:
  31. traverse the pointer belonging to this bit
  32. ]#
  33. when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
  34. type
  35. PCell = Cell
  36. when not declaredInScope(PageShift):
  37. include bitmasks
  38. else:
  39. type
  40. RefCount = int
  41. Cell {.pure.} = object
  42. refcount: RefCount # the refcount and some flags
  43. typ: PNimType
  44. when trackAllocationSource:
  45. filename: cstring
  46. line: int
  47. when useCellIds:
  48. id: int
  49. PCell = ptr Cell
  50. type
  51. PPageDesc = ptr PageDesc
  52. BitIndex = range[0..UnitsPerPage-1]
  53. PageDesc {.final, pure.} = object
  54. next: PPageDesc # all nodes are connected with this pointer
  55. key: uint # start address at bit 0
  56. bits: array[BitIndex, int] # a bit vector
  57. PPageDescArray = ptr UncheckedArray[PPageDesc]
  58. CellSet {.final, pure.} = object
  59. counter, max: int
  60. head: PPageDesc
  61. data: PPageDescArray
  62. when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
  63. discard
  64. else:
  65. include cellseqs_v1
  66. # ------------------- cell set handling ---------------------------------------
  67. const
  68. InitCellSetSize = 1024 # must be a power of two!
  69. proc init(s: var CellSet) =
  70. s.data = cast[PPageDescArray](alloc0(InitCellSetSize * sizeof(PPageDesc)))
  71. s.max = InitCellSetSize-1
  72. s.counter = 0
  73. s.head = nil
  74. proc deinit(s: var CellSet) =
  75. var it = s.head
  76. while it != nil:
  77. var n = it.next
  78. dealloc(it)
  79. it = n
  80. s.head = nil # play it safe here
  81. dealloc(s.data)
  82. s.data = nil
  83. s.counter = 0
  84. proc nextTry(h, maxHash: int): int {.inline.} =
  85. result = ((5*h) + 1) and maxHash
  86. # For any initial h in range(maxHash), repeating that maxHash times
  87. # generates each int in range(maxHash) exactly once (see any text on
  88. # random-number generation for proof).
  89. proc cellSetGet(t: CellSet, key: uint): PPageDesc =
  90. var h = cast[int](key) and t.max
  91. while t.data[h] != nil:
  92. if t.data[h].key == key: return t.data[h]
  93. h = nextTry(h, t.max)
  94. return nil
  95. proc cellSetRawInsert(t: CellSet, data: PPageDescArray, desc: PPageDesc) =
  96. var h = cast[int](desc.key) and t.max
  97. while data[h] != nil:
  98. sysAssert(data[h] != desc, "CellSetRawInsert 1")
  99. h = nextTry(h, t.max)
  100. sysAssert(data[h] == nil, "CellSetRawInsert 2")
  101. data[h] = desc
  102. proc cellSetEnlarge(t: var CellSet) =
  103. var oldMax = t.max
  104. t.max = ((t.max+1)*2)-1
  105. var n = cast[PPageDescArray](alloc0((t.max + 1) * sizeof(PPageDesc)))
  106. for i in 0 .. oldMax:
  107. if t.data[i] != nil:
  108. cellSetRawInsert(t, n, t.data[i])
  109. dealloc(t.data)
  110. t.data = n
  111. proc cellSetPut(t: var CellSet, key: uint): PPageDesc =
  112. var h = cast[int](key) and t.max
  113. while true:
  114. var x = t.data[h]
  115. if x == nil: break
  116. if x.key == key: return x
  117. h = nextTry(h, t.max)
  118. if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
  119. cellSetEnlarge(t)
  120. inc(t.counter)
  121. h = cast[int](key) and t.max
  122. while t.data[h] != nil: h = nextTry(h, t.max)
  123. sysAssert(t.data[h] == nil, "CellSetPut")
  124. # the new page descriptor goes into result
  125. result = cast[PPageDesc](alloc0(sizeof(PageDesc)))
  126. result.next = t.head
  127. result.key = key
  128. t.head = result
  129. t.data[h] = result
  130. # ---------- slightly higher level procs --------------------------------------
  131. proc contains(s: CellSet, cell: PCell): bool =
  132. var u = cast[uint](cell)
  133. var t = cellSetGet(s, u shr PageShift)
  134. if t != nil:
  135. u = (u mod PageSize) div MemAlign
  136. result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
  137. else:
  138. result = false
  139. proc incl(s: var CellSet, cell: PCell) =
  140. var u = cast[uint](cell)
  141. var t = cellSetPut(s, u shr PageShift)
  142. u = (u mod PageSize) div MemAlign
  143. t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
  144. proc excl(s: var CellSet, cell: PCell) =
  145. var u = cast[uint](cell)
  146. var t = cellSetGet(s, u shr PageShift)
  147. if t != nil:
  148. u = (u mod PageSize) div MemAlign
  149. t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
  150. not (1 shl (u and IntMask)))
  151. proc containsOrIncl(s: var CellSet, cell: PCell): bool =
  152. var u = cast[uint](cell)
  153. var t = cellSetGet(s, u shr PageShift)
  154. if t != nil:
  155. u = (u mod PageSize) div MemAlign
  156. result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
  157. if not result:
  158. t.bits[u shr IntShift] = t.bits[u shr IntShift] or
  159. (1 shl (u and IntMask))
  160. else:
  161. incl(s, cell)
  162. result = false
  163. iterator elements(t: CellSet): PCell {.inline.} =
  164. # while traversing it is forbidden to add pointers to the tree!
  165. var r = t.head
  166. while r != nil:
  167. var i: uint = 0
  168. while int(i) <= high(r.bits):
  169. var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
  170. # modifying operations are not allowed during traversation
  171. var j: uint = 0
  172. while w != 0: # test all remaining bits for zero
  173. if (w and 1) != 0: # the bit is set!
  174. yield cast[PCell]((r.key shl PageShift) or
  175. (i shl IntShift + j) * MemAlign)
  176. inc(j)
  177. w = w shr 1
  178. inc(i)
  179. r = r.next
  180. when false:
  181. type
  182. CellSetIter = object
  183. p: PPageDesc
  184. i, w, j: int
  185. proc next(it: var CellSetIter): PCell =
  186. while true:
  187. while it.w != 0: # test all remaining bits for zero
  188. if (it.w and 1) != 0: # the bit is set!
  189. result = cast[PCell]((it.p.key shl PageShift) or
  190. (it.i shl IntShift +% it.j) *% MemAlign)
  191. inc(it.j)
  192. it.w = it.w shr 1
  193. return
  194. else:
  195. inc(it.j)
  196. it.w = it.w shr 1
  197. # load next w:
  198. if it.i >= high(it.p.bits):
  199. it.i = 0
  200. it.j = 0
  201. it.p = it.p.next
  202. if it.p == nil: return nil
  203. else:
  204. inc it.i
  205. it.w = it.p.bits[i]
  206. proc init(it: var CellSetIter; t: CellSet): PCell =
  207. it.p = t.head
  208. it.i = -1
  209. it.w = 0
  210. result = it.next
  211. iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
  212. var r = t.head
  213. while r != nil:
  214. let ss = cellSetGet(s, r.key)
  215. var i:uint = 0
  216. while int(i) <= high(r.bits):
  217. var w = r.bits[i]
  218. if ss != nil:
  219. w = w and not ss.bits[i]
  220. var j:uint = 0
  221. while w != 0:
  222. if (w and 1) != 0:
  223. yield cast[PCell]((r.key shl PageShift) or
  224. (i shl IntShift + j) * MemAlign)
  225. inc(j)
  226. w = w shr 1
  227. inc(i)
  228. r = r.next