gcbench.nim 5.6 KB

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