thamming_orc.nim 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. discard """
  2. output: '''(allocCount: 1114, deallocCount: 1112)
  3. created 491 destroyed 491'''
  4. cmd: "nim c --gc:orc -d:nimAllocStats $file"
  5. """
  6. # bug #18421
  7. # test Nim Hamming Number Lazy List algo with reference counts and not...
  8. # compile with "-d:release -d:danger" and test with various
  9. # memory managment GC's, allocators, threading, etc.
  10. # it should be guaranteed to work with zero memory leaks with `--gc:orc`...
  11. # compile with `-d:trace20` to trace creation and destruction of first 20 values.
  12. from math import log2
  13. # implement our own basic BigInt so the bigints library isn't necessary...
  14. type
  15. BigInt = object
  16. digits: seq[uint32]
  17. let zeroBigInt = BigInt(digits: @[ 0'u32 ])
  18. let oneBigInt = BigInt(digits: @[ 1'u32 ])
  19. proc shladd(bi: var BigInt; n: int; a: BigInt) =
  20. # assume that both `bi` and `a` are sized correctly with
  21. # msuint32 for both not containing a zero
  22. let alen = a.digits.len
  23. let mx = max(bi.digits.len, a.digits.len)
  24. for i in bi.digits.len ..< mx: bi.digits.add 0'u32
  25. var cry = 0'u64
  26. for i in 0 ..< alen:
  27. cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64
  28. bi.digits[i] = cry.uint32; cry = cry shr 32
  29. for i in alen ..< mx:
  30. cry += bi.digits[i].uint64 shl n
  31. bi.digits[i] = cry.uint32; cry = cry shr 32
  32. if cry > 0'u64:
  33. bi.digits.add cry.uint32
  34. proc `$`(x: BigInt): string =
  35. if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32):
  36. return "0"
  37. result = ""; var n = x; var msd = n.digits.high
  38. while msd >= 0:
  39. if n.digits[msd] == 0'u32: msd.dec; continue
  40. var brw = 0.uint64
  41. for i in countdown(msd, 0):
  42. let dvdnd = n.digits[i].uint64 + (brw shl 32)
  43. let q = dvdnd div 10'u64; brw = dvdnd - q * 10'u64
  44. n.digits[i] = q.uint32
  45. result &= $brw
  46. for i in 0 .. result.high shr 1: # reverse result string in place
  47. let tmp = result[^(i + 1)]
  48. result[^(i + 1)] = result[i]
  49. result[i] = tmp
  50. type TriVal = (uint32, uint32, uint32)
  51. type LogRep = (float64, TriVal)
  52. type LogRepf = proc(x: LogRep): LogRep
  53. const one: LogRep = (0.0'f64, (0'u32, 0'u32, 0'u32))
  54. proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0]
  55. proc convertTriVal2BigInt(tpl: TriVal): BigInt =
  56. result = oneBigInt
  57. let (x2, x3, x5) = tpl
  58. for _ in 1 .. x2: result.shladd 1, zeroBigInt
  59. for _ in 1 .. x3: result.shladd 1, result
  60. for _ in 1 .. x5: result.shladd 2, result
  61. const lb2 = 1.0'f64
  62. const lb3 = 3.0'f64.log2
  63. const lb5 = 5.0'f64.log2
  64. proc mul2(me: LogRep): LogRep =
  65. let (lr, tpl) = me; let (x2, x3, x5) = tpl
  66. (lr + lb2, (x2 + 1, x3, x5))
  67. proc mul3(me: LogRep): LogRep =
  68. let (lr, tpl) = me; let (x2, x3, x5) = tpl
  69. (lr + lb3, (x2, x3 + 1, x5))
  70. proc mul5(me: LogRep): LogRep =
  71. let (lr, tpl) = me; let (x2, x3, x5) = tpl
  72. (lr + lb5, (x2, x3, x5 + 1))
  73. type
  74. LazyListObj = object
  75. hd: LogRep
  76. tlf: proc(): LazyList {.closure.}
  77. tl: LazyList
  78. LazyList = ref LazyListObj
  79. var destroyed = 0
  80. proc `=destroy`(ll: var LazyListObj) =
  81. destroyed += 1
  82. if ll.tlf == nil and ll.tl == nil: return
  83. when defined(trace20):
  84. echo "destroying: ", (destroyed, ll.hd[1].convertTriVal2BigInt)
  85. if ll.tlf != nil: ll.tlf.`=destroy`
  86. if ll.tl != nil: ll.tl.`=destroy`
  87. #wasMoved(ll)
  88. proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk
  89. if ll.tlf != nil: ll.tl = ll.tlf(); ll.tlf = nil
  90. ll.tl
  91. var created = 0
  92. iterator hammings(until: int): TriVal =
  93. proc merge(x, y: LazyList): LazyList =
  94. let xh = x.hd; let yh = y.hd; created += 1
  95. when defined(trace20):
  96. echo "merge create: ", (created - 1, (if xh < yh: xh else: yh)[1].convertTriVal2BigInt)
  97. if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y)
  98. else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest)
  99. proc smult(mltf: LogRepf; s: LazyList): LazyList =
  100. proc smults(ss: LazyList): LazyList =
  101. when defined(trace20):
  102. echo "mult create: ", (created, ss.hd.mltf[1].convertTriVal2BigInt)
  103. created += 1; LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults)
  104. s.smults
  105. proc unnsm(s: LazyList, mltf: LogRepf): LazyList =
  106. var r: LazyList = nil
  107. when defined(trace20):
  108. echo "first create: ", (created, one[1].convertTriVal2BigInt)
  109. let frst = LazyList(hd: one, tlf: proc(): LazyList = r); created += 1
  110. r = if s == nil: smult(mltf, frst) else: s.merge smult(mltf, frst)
  111. r
  112. yield one[1]
  113. var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2
  114. for _ in 2 .. until:
  115. yield hmpll.hd[1]; hmpll = hmpll.rest # almost forever
  116. proc main =
  117. var s = ""
  118. for h in hammings(20): s &= $h.convertTrival2BigInt & " "
  119. doAssert s == "1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 ",
  120. "Algorithmic error finding first 20 Hamming numbers!!!"
  121. when not defined(trace20):
  122. var lsth: TriVal
  123. for h in hammings(200): lsth = h
  124. doAssert $lsth.convertTriVal2BigInt == "16200",
  125. "Algorithmic error finding 200th Hamming number!!!"
  126. let mem = getOccupiedMem()
  127. main()
  128. GC_FullCollect()
  129. let mb = getOccupiedMem() - mem
  130. doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!"
  131. echo getAllocStats()
  132. echo "created ", created, " destroyed ", destroyed