gcbench.nim 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. discard """
  2. outputsub: "Success!"
  3. """
  4. # This is adapted from a benchmark written by John Ellis and Pete Kovac
  5. # of Post Communications.
  6. # It was modified by Hans Boehm of Silicon Graphics.
  7. #
  8. # This is no substitute for real applications. No actual application
  9. # is likely to behave in exactly this way. However, this benchmark was
  10. # designed to be more representative of real applications than other
  11. # Java GC benchmarks of which we are aware.
  12. # It attempts to model those properties of allocation requests that
  13. # are important to current GC techniques.
  14. # It is designed to be used either to obtain a single overall performance
  15. # number, or to give a more detailed estimate of how collector
  16. # performance varies with object lifetimes. It prints the time
  17. # required to allocate and collect balanced binary trees of various
  18. # sizes. Smaller trees result in shorter object lifetimes. Each cycle
  19. # allocates roughly the same amount of memory.
  20. # Two data structures are kept around during the entire process, so
  21. # that the measured performance is representative of applications
  22. # that maintain some live in-memory data. One of these is a tree
  23. # containing many pointers. The other is a large array containing
  24. # double precision floating point numbers. Both should be of comparable
  25. # size.
  26. #
  27. # The results are only really meaningful together with a specification
  28. # of how much memory was used. It is possible to trade memory for
  29. # better time performance. This benchmark should be run in a 32 MB
  30. # heap, though we don't currently know how to enforce that uniformly.
  31. #
  32. # Unlike the original Ellis and Kovac benchmark, we do not attempt
  33. # measure pause times. This facility should eventually be added back
  34. # in. There are several reasons for omitting it for now. The original
  35. # implementation depended on assumptions about the thread scheduler
  36. # that don't hold uniformly. The results really measure both the
  37. # scheduler and GC. Pause time measurements tend to not fit well with
  38. # current benchmark suites. As far as we know, none of the current
  39. # commercial Java implementations seriously attempt to minimize GC pause
  40. # times.
  41. #
  42. # Known deficiencies:
  43. # - No way to check on memory use
  44. # - No cyclic data structures
  45. # - No attempt to measure variation with object size
  46. # - Results are sensitive to locking cost, but we don't
  47. # check for proper locking
  48. #
  49. import
  50. strutils, times
  51. type
  52. PNode = ref TNode
  53. TNode {.final, acyclic.} = object
  54. left, right: PNode
  55. i, j: int
  56. proc newNode(L, r: sink PNode): PNode =
  57. new(result)
  58. result.left = L
  59. result.right = r
  60. const
  61. kStretchTreeDepth = 18 # about 16Mb
  62. kLongLivedTreeDepth = 16 # about 4Mb
  63. kArraySize = 500000 # about 4Mb
  64. kMinTreeDepth = 4
  65. kMaxTreeDepth = 16
  66. when not declared(withScratchRegion):
  67. template withScratchRegion(body: untyped) = body
  68. # Nodes used by a tree of a given size
  69. proc treeSize(i: int): int = return ((1 shl (i + 1)) - 1)
  70. # Number of iterations to use for a given tree depth
  71. proc numIters(i: int): int =
  72. return 2 * treeSize(kStretchTreeDepth) div treeSize(i)
  73. # Build tree top down, assigning to older objects.
  74. proc populate(iDepth: int, thisNode: PNode) =
  75. if iDepth <= 0:
  76. return
  77. else:
  78. new(thisNode.left)
  79. new(thisNode.right)
  80. populate(iDepth-1, thisNode.left)
  81. populate(iDepth-1, thisNode.right)
  82. # Build tree bottom-up
  83. proc makeTree(iDepth: int): PNode =
  84. if iDepth <= 0:
  85. new(result)
  86. else:
  87. return newNode(makeTree(iDepth-1), makeTree(iDepth-1))
  88. proc printDiagnostics() =
  89. echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
  90. echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
  91. proc timeConstruction(depth: int) =
  92. var
  93. root, tempTree: PNode
  94. iNumIters: int
  95. iNumIters = numIters(depth)
  96. echo("Creating " & $iNumIters & " trees of depth " & $depth)
  97. var t = epochTime()
  98. for i in 0..iNumIters-1:
  99. new(tempTree)
  100. populate(depth, tempTree)
  101. tempTree = nil
  102. echo("\tTop down construction took " & $(epochTime() - t) & "msecs")
  103. t = epochTime()
  104. for i in 0..iNumIters-1:
  105. tempTree = makeTree(depth)
  106. tempTree = nil
  107. echo("\tBottom up construction took " & $(epochTime() - t) & "msecs")
  108. type
  109. tMyArray = seq[float]
  110. proc main() =
  111. var
  112. root, longLivedTree, tempTree: PNode
  113. myarray: tMyArray
  114. echo("Garbage Collector Test")
  115. echo(" Stretching memory with a binary tree of depth " & $kStretchTreeDepth)
  116. printDiagnostics()
  117. var t = epochTime()
  118. # Stretch the memory space quickly
  119. withScratchRegion:
  120. tempTree = makeTree(kStretchTreeDepth)
  121. tempTree = nil
  122. # Create a long lived object
  123. echo(" Creating a long-lived binary tree of depth " &
  124. $kLongLivedTreeDepth)
  125. new(longLivedTree)
  126. populate(kLongLivedTreeDepth, longLivedTree)
  127. # Create long-lived array, filling half of it
  128. echo(" Creating a long-lived array of " & $kArraySize & " doubles")
  129. withScratchRegion:
  130. newSeq(myarray, kArraySize)
  131. for i in 0..kArraySize div 2 - 1:
  132. myarray[i] = 1.0 / toFloat(i)
  133. printDiagnostics()
  134. var d = kMinTreeDepth
  135. while d <= kMaxTreeDepth:
  136. withScratchRegion:
  137. timeConstruction(d)
  138. inc(d, 2)
  139. if longLivedTree == nil or myarray[1000] != 1.0/1000.0:
  140. echo("Failed")
  141. # fake reference to LongLivedTree
  142. # and array to keep them from being optimized away
  143. var elapsed = epochTime() - t
  144. printDiagnostics()
  145. echo("Completed in " & $elapsed & "s. Success!")
  146. when declared(getMaxMem):
  147. echo "Max memory ", formatSize getMaxMem()
  148. when defined(GC_setMaxPause):
  149. GC_setMaxPause 2_000
  150. when defined(gcDestructors):
  151. let mem = getOccupiedMem()
  152. main()
  153. when defined(gcDestructors):
  154. doAssert getOccupiedMem() == mem