mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-30 09:54:49 +00:00
181 lines
5.6 KiB
Nim
181 lines
5.6 KiB
Nim
discard """
|
|
outputsub: "Success!"
|
|
"""
|
|
|
|
# This is adapted from a benchmark written by John Ellis and Pete Kovac
|
|
# of Post Communications.
|
|
# It was modified by Hans Boehm of Silicon Graphics.
|
|
#
|
|
# This is no substitute for real applications. No actual application
|
|
# is likely to behave in exactly this way. However, this benchmark was
|
|
# designed to be more representative of real applications than other
|
|
# Java GC benchmarks of which we are aware.
|
|
# It attempts to model those properties of allocation requests that
|
|
# are important to current GC techniques.
|
|
# It is designed to be used either to obtain a single overall performance
|
|
# number, or to give a more detailed estimate of how collector
|
|
# performance varies with object lifetimes. It prints the time
|
|
# required to allocate and collect balanced binary trees of various
|
|
# sizes. Smaller trees result in shorter object lifetimes. Each cycle
|
|
# allocates roughly the same amount of memory.
|
|
# Two data structures are kept around during the entire process, so
|
|
# that the measured performance is representative of applications
|
|
# that maintain some live in-memory data. One of these is a tree
|
|
# containing many pointers. The other is a large array containing
|
|
# double precision floating point numbers. Both should be of comparable
|
|
# size.
|
|
#
|
|
# The results are only really meaningful together with a specification
|
|
# of how much memory was used. It is possible to trade memory for
|
|
# better time performance. This benchmark should be run in a 32 MB
|
|
# heap, though we don't currently know how to enforce that uniformly.
|
|
#
|
|
# Unlike the original Ellis and Kovac benchmark, we do not attempt
|
|
# measure pause times. This facility should eventually be added back
|
|
# in. There are several reasons for omitting it for now. The original
|
|
# implementation depended on assumptions about the thread scheduler
|
|
# that don't hold uniformly. The results really measure both the
|
|
# scheduler and GC. Pause time measurements tend to not fit well with
|
|
# current benchmark suites. As far as we know, none of the current
|
|
# commercial Java implementations seriously attempt to minimize GC pause
|
|
# times.
|
|
#
|
|
# Known deficiencies:
|
|
# - No way to check on memory use
|
|
# - No cyclic data structures
|
|
# - No attempt to measure variation with object size
|
|
# - Results are sensitive to locking cost, but we don't
|
|
# check for proper locking
|
|
#
|
|
|
|
import
|
|
strutils, times
|
|
|
|
type
|
|
PNode = ref TNode
|
|
TNode {.final, acyclic.} = object
|
|
left, right: PNode
|
|
i, j: int
|
|
|
|
proc newNode(L, r: sink PNode): PNode =
|
|
new(result)
|
|
result.left = L
|
|
result.right = r
|
|
|
|
const
|
|
kStretchTreeDepth = 18 # about 16Mb
|
|
kLongLivedTreeDepth = 16 # about 4Mb
|
|
kArraySize = 500000 # about 4Mb
|
|
kMinTreeDepth = 4
|
|
kMaxTreeDepth = 16
|
|
|
|
when not declared(withScratchRegion):
|
|
template withScratchRegion(body: untyped) = body
|
|
|
|
# Nodes used by a tree of a given size
|
|
proc treeSize(i: int): int = return ((1 shl (i + 1)) - 1)
|
|
|
|
# Number of iterations to use for a given tree depth
|
|
proc numIters(i: int): int =
|
|
return 2 * treeSize(kStretchTreeDepth) div treeSize(i)
|
|
|
|
# Build tree top down, assigning to older objects.
|
|
proc populate(iDepth: int, thisNode: PNode) =
|
|
if iDepth <= 0:
|
|
return
|
|
else:
|
|
new(thisNode.left)
|
|
new(thisNode.right)
|
|
populate(iDepth-1, thisNode.left)
|
|
populate(iDepth-1, thisNode.right)
|
|
|
|
# Build tree bottom-up
|
|
proc makeTree(iDepth: int): PNode =
|
|
if iDepth <= 0:
|
|
new(result)
|
|
else:
|
|
return newNode(makeTree(iDepth-1), makeTree(iDepth-1))
|
|
|
|
proc printDiagnostics() =
|
|
echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
|
|
echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
|
|
|
|
proc timeConstruction(depth: int) =
|
|
var
|
|
root, tempTree: PNode
|
|
iNumIters: int
|
|
|
|
iNumIters = numIters(depth)
|
|
|
|
echo("Creating " & $iNumIters & " trees of depth " & $depth)
|
|
var t = epochTime()
|
|
for i in 0..iNumIters-1:
|
|
new(tempTree)
|
|
populate(depth, tempTree)
|
|
tempTree = nil
|
|
echo("\tTop down construction took " & $(epochTime() - t) & "msecs")
|
|
t = epochTime()
|
|
for i in 0..iNumIters-1:
|
|
tempTree = makeTree(depth)
|
|
tempTree = nil
|
|
echo("\tBottom up construction took " & $(epochTime() - t) & "msecs")
|
|
|
|
type
|
|
tMyArray = seq[float]
|
|
|
|
proc main() =
|
|
var
|
|
root, longLivedTree, tempTree: PNode
|
|
myarray: tMyArray
|
|
|
|
echo("Garbage Collector Test")
|
|
echo(" Stretching memory with a binary tree of depth " & $kStretchTreeDepth)
|
|
printDiagnostics()
|
|
var t = epochTime()
|
|
|
|
# Stretch the memory space quickly
|
|
withScratchRegion:
|
|
tempTree = makeTree(kStretchTreeDepth)
|
|
tempTree = nil
|
|
|
|
# Create a long lived object
|
|
echo(" Creating a long-lived binary tree of depth " &
|
|
$kLongLivedTreeDepth)
|
|
new(longLivedTree)
|
|
populate(kLongLivedTreeDepth, longLivedTree)
|
|
|
|
# Create long-lived array, filling half of it
|
|
echo(" Creating a long-lived array of " & $kArraySize & " doubles")
|
|
withScratchRegion:
|
|
newSeq(myarray, kArraySize)
|
|
for i in 0..kArraySize div 2 - 1:
|
|
myarray[i] = 1.0 / toFloat(i)
|
|
|
|
printDiagnostics()
|
|
|
|
var d = kMinTreeDepth
|
|
while d <= kMaxTreeDepth:
|
|
withScratchRegion:
|
|
timeConstruction(d)
|
|
inc(d, 2)
|
|
|
|
if longLivedTree == nil or myarray[1000] != 1.0/1000.0:
|
|
echo("Failed")
|
|
# fake reference to LongLivedTree
|
|
# and array to keep them from being optimized away
|
|
|
|
var elapsed = epochTime() - t
|
|
printDiagnostics()
|
|
echo("Completed in " & $elapsed & "s. Success!")
|
|
when declared(getMaxMem):
|
|
echo "Max memory ", formatSize getMaxMem()
|
|
|
|
when defined(GC_setMaxPause):
|
|
GC_setMaxPause 2_000
|
|
|
|
when defined(gcDestructors):
|
|
let mem = getOccupiedMem()
|
|
main()
|
|
when defined(gcDestructors):
|
|
doAssert getOccupiedMem() == mem
|