gc tweaking to gain a few percent of performance

This commit is contained in:
Araq 2011-05-07 22:13:24 +02:00
commit 73c3551766
13 changed files with 183 additions and 153 deletions

View file

@ -677,8 +677,8 @@ proc genProcAux(m: BModule, prc: PSym) =
if optStackTrace in prc.options: app(generatedProc, deinitFrame(p)) if optStackTrace in prc.options: app(generatedProc, deinitFrame(p))
if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM): if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM):
appf(generatedProc, appf(generatedProc,
"profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n", "profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n",
[toRope(prc.loc.a)]) [toRope(prc.loc.a)])
app(generatedProc, returnStmt) app(generatedProc, returnStmt)
app(generatedProc, '}' & tnl) app(generatedProc, '}' & tnl)
app(m.s[cfsProcs], generatedProc) app(m.s[cfsProcs], generatedProc)

View file

@ -100,19 +100,16 @@ proc crcFromBuf(buf: Pointer, length: int): TCrc32 =
proc crcFromFile(filename: string): TCrc32 = proc crcFromFile(filename: string): TCrc32 =
const const
bufSize = 8 * 1024 bufSize = 8000 # don't use 8K for the memory allocator!
var var
bin: tfile bin: tfile
buf: Pointer
readBytes: int
p: PByteArray
result = InitCrc32 result = InitCrc32
if not open(bin, filename): if not open(bin, filename):
return # not equal if file does not exist return # not equal if file does not exist
buf = alloc(BufSize) var buf = alloc(BufSize)
p = cast[PByteArray](buf) var p = cast[PByteArray](buf)
while true: while true:
readBytes = readBuffer(bin, buf, bufSize) var readBytes = readBuffer(bin, buf, bufSize)
for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result) for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result)
if readBytes != bufSize: break if readBytes != bufSize: break
dealloc(buf) dealloc(buf)

View file

@ -50,7 +50,7 @@ proc ProcessCmdLine(pass: TCmdLinePass, command, filename: var string) =
rawMessage(errArgsNeedRunOption, []) rawMessage(errArgsNeedRunOption, [])
proc HandleCmdLine() = proc HandleCmdLine() =
var start = getTime() var start = epochTime()
if paramCount() == 0: if paramCount() == 0:
writeCommandLineUsage() writeCommandLineUsage()
else: else:
@ -73,7 +73,8 @@ proc HandleCmdLine() =
if gCmd == cmdRun: if gCmd == cmdRun:
tccgen.run() tccgen.run()
if gCmd notin {cmdInterpret, cmdRun}: if gCmd notin {cmdInterpret, cmdRun}:
rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start)]) rawMessage(hintSuccessX, [$gLinesCompiled,
formatFloat(epochTime() - start, ffDecimal, 3)])
if optRun in gGlobalOptions: if optRun in gGlobalOptions:
when defined(unix): when defined(unix):
var prog = "./" & quoteIfContainsWhite(changeFileExt(filename, "")) var prog = "./" & quoteIfContainsWhite(changeFileExt(filename, ""))
@ -81,6 +82,7 @@ proc HandleCmdLine() =
var prog = quoteIfContainsWhite(changeFileExt(filename, "")) var prog = quoteIfContainsWhite(changeFileExt(filename, ""))
execExternalProgram(prog & ' ' & arguments) execExternalProgram(prog & ' ' & arguments)
#GC_disableMarkAndSweep()
cmdLineInfo = newLineInfo("command line", -1, -1) cmdLineInfo = newLineInfo("command line", -1, -1)
condsyms.InitDefines() condsyms.InitDefines()
HandleCmdLine() HandleCmdLine()

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -12,13 +12,7 @@
import import
nhashes, ast, astalgo, types nhashes, ast, astalgo, types
proc NodeTableGet*(t: TNodeTable, key: PNode): int
proc NodeTablePut*(t: var TNodeTable, key: PNode, val: int)
proc NodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int
# implementation
proc hashTree(n: PNode): THash = proc hashTree(n: PNode): THash =
result = 0
if n == nil: return if n == nil: return
result = ord(n.kind) result = ord(n.kind)
case n.kind case n.kind
@ -41,7 +35,6 @@ proc hashTree(n: PNode): THash =
result = concHash(result, hashTree(n.sons[i])) result = concHash(result, hashTree(n.sons[i]))
proc TreesEquivalent(a, b: PNode): bool = proc TreesEquivalent(a, b: PNode): bool =
result = false
if a == b: if a == b:
result = true result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind): elif (a != nil) and (b != nil) and (a.kind == b.kind):
@ -60,36 +53,31 @@ proc TreesEquivalent(a, b: PNode): bool =
if result: result = sameTypeOrNil(a.typ, b.typ) if result: result = sameTypeOrNil(a.typ, b.typ)
proc NodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int = proc NodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int =
var h: THash var h: THash = k and high(t.data)
h = k and high(t.data)
while t.data[h].key != nil: while t.data[h].key != nil:
if (t.data[h].h == k) and TreesEquivalent(t.data[h].key, key): if (t.data[h].h == k) and TreesEquivalent(t.data[h].key, key):
return h return h
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = - 1 result = -1
proc NodeTableGet(t: TNodeTable, key: PNode): int = proc NodeTableGet*(t: TNodeTable, key: PNode): int =
var index: int var index = NodeTableRawGet(t, hashTree(key), key)
index = NodeTableRawGet(t, hashTree(key), key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: result = low(int) else: result = low(int)
proc NodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode, val: int) = proc NodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
var h: THash val: int) =
h = k and high(data) var h: THash = k and high(data)
while data[h].key != nil: h = nextTry(h, high(data)) while data[h].key != nil: h = nextTry(h, high(data))
assert(data[h].key == nil) assert(data[h].key == nil)
data[h].h = k data[h].h = k
data[h].key = key data[h].key = key
data[h].val = val data[h].val = val
proc NodeTablePut(t: var TNodeTable, key: PNode, val: int) = proc NodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
var var n: TNodePairSeq
index: int var k: THash = hashTree(key)
n: TNodePairSeq var index = NodeTableRawGet(t, k, key)
k: THash
k = hashTree(key)
index = NodeTableRawGet(t, k, key)
if index >= 0: if index >= 0:
assert(t.data[index].key != nil) assert(t.data[index].key != nil)
t.data[index].val = val t.data[index].val = val
@ -103,13 +91,10 @@ proc NodeTablePut(t: var TNodeTable, key: PNode, val: int) =
NodeTableRawInsert(t.data, k, key, val) NodeTableRawInsert(t.data, k, key, val)
inc(t.counter) inc(t.counter)
proc NodeTableTestOrSet(t: var TNodeTable, key: PNode, val: int): int = proc NodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
var var n: TNodePairSeq
index: int var k: THash = hashTree(key)
n: TNodePairSeq var index = NodeTableRawGet(t, k, key)
k: THash
k = hashTree(key)
index = NodeTableRawGet(t, k, key)
if index >= 0: if index >= 0:
assert(t.data[index].key != nil) assert(t.data[index].key != nil)
result = t.data[index].val result = t.data[index].val

View file

@ -157,10 +157,8 @@ proc cleanAux(dir: string) =
of "nimcache": of "nimcache":
echo "removing dir: ", path echo "removing dir: ", path
removeDir(path) removeDir(path)
of "dist", ".git": of "dist", ".git": nil
nil else: cleanAux(path)
else:
cleanAux(path)
else: nil else: nil
proc removePattern(pattern: string) = proc removePattern(pattern: string) =

View file

@ -100,7 +100,7 @@ when defined(Windows):
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
proc WaitForSingleObject(hHandle: THANDLE, dwMilliseconds: int32): int32 {. proc WaitForSingleObject(hHandle: THANDLE, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
proc TerminateThread(hThread: THandle, dwExitCode: int32): int32 {. proc TerminateThread(hThread: THandle, dwExitCode: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "TerminateThread".} stdcall, dynlib: "kernel32", importc: "TerminateThread".}

View file

@ -216,9 +216,16 @@ proc `[]=`*[A, B](t: TOrderedHashTable[A, B], key: A, val: B) =
inc(t.counter) inc(t.counter)
proc del*[A, B](t: TOrderedHashTable[A, B], key: A) = proc del*[A, B](t: TOrderedHashTable[A, B], key: A) =
## deletes `key` from hash table `t`. ## deletes `key` from hash table `t`. Warning: It's inefficient for ordered
## tables: O(n).
var index = RawGet(t, key) var index = RawGet(t, key)
if index >= 0: if index >= 0:
var i = t.first
while i >= 0:
var nxt = t.data[i].next
if nxt == index: XXX
i = nxt
t.data[index].slot = seDeleted t.data[index].slot = seDeleted
dec(t.counter) dec(t.counter)

View file

@ -23,28 +23,39 @@ proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline.} proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline.}
# Support for thread local storage: # Support for thread local storage:
when false: when defined(windows):
when defined(windows): type
proc TlsAlloc(): int32 {.importc: "TlsAlloc", stdcall, dynlib: "kernel32".} TThreadVarSlot {.compilerproc.} = distinct int32
proc TlsSetValue(dwTlsIndex: int32, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc TlsGetValue(dwTlsIndex: int32): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
else: proc TlsAlloc(): TThreadVarSlot {.
type importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
Tpthread_key {.importc: "pthread_key_t", header: "<sys/types.h>".} = int proc TlsSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc TlsGetValue(dwTlsIndex: TThreadVarSlot): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
proc pthread_getspecific(a1: Tpthread_key): pointer {. proc ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} =
importc: "pthread_getspecific", header: "<pthread.h>".} result = TlsAlloc()
proc pthread_key_create(a1: ptr Tpthread_key, proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.compilerproc.} =
a2: proc (x: pointer) {.noconv.}): int32 {. TlsSetValue(s, value)
importc: "pthread_key_create", header: "<pthread.h>".} proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc.} =
proc pthread_key_delete(a1: Tpthread_key): int32 {. result = TlsGetValue(s)
importc: "pthread_key_delete", header: "<pthread.h>".}
proc pthread_setspecific(a1: Tpthread_key, a2: pointer): int32 {. else:
importc: "pthread_setspecific", header: "<pthread.h>".} type
Tpthread_key {.importc: "pthread_key_t",
header: "<sys/types.h>".} = distinct int
TThreadVarSlot {.compilerproc.} = Tpthread_key
proc pthread_getspecific(a1: Tpthread_key): pointer {.
importc: "pthread_getspecific", header: "<pthread.h>".}
proc pthread_key_create(a1: ptr Tpthread_key,
destruct: proc (x: pointer) {.noconv.}): int32 {.
importc: "pthread_key_create", header: "<pthread.h>".}
proc pthread_key_delete(a1: Tpthread_key): int32 {.
importc: "pthread_key_delete", header: "<pthread.h>".}
proc pthread_setspecific(a1: Tpthread_key, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".}

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -21,8 +21,6 @@ proc rawWrite(f: TFile, s: string) =
proc nimLoadLibraryError(path: string) = proc nimLoadLibraryError(path: string) =
# carefully written to avoid memory allocation: # carefully written to avoid memory allocation:
#stdout.write("could not load: ")
#quit(path)
stdout.rawWrite("could not load: ") stdout.rawWrite("could not load: ")
stdout.rawWrite(path) stdout.rawWrite(path)
stdout.rawWrite("\n") stdout.rawWrite("\n")

View file

@ -23,7 +23,7 @@
const const
CycleIncrease = 2 # is a multiplicative increase CycleIncrease = 2 # is a multiplicative increase
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
ZctThreshold = 256 # we collect garbage if the ZCT's size ZctThreshold = 500 # we collect garbage if the ZCT's size
# reaches this threshold # reaches this threshold
# this seems to be a good value # this seems to be a good value
@ -294,31 +294,32 @@ proc initGC() =
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
var d = cast[TAddress](dest) var d = cast[TAddress](dest)
case n.kind case n.kind
of nkNone: assert(false)
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
of nkList: of nkList:
for i in 0..n.len-1: forAllSlotsAux(dest, n.sons[i], op) for i in 0..n.len-1: forAllSlotsAux(dest, n.sons[i], op)
of nkCase: of nkCase:
var m = selectBranch(dest, n) var m = selectBranch(dest, n)
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: assert(false)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
var d = cast[TAddress](dest) var d = cast[TAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
case mt.Kind case mt.Kind
of tyArray, tyArrayConstr, tyOpenArray:
for i in 0..(mt.size div mt.base.size)-1:
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
of tyRef, tyString, tySequence: # leaf: of tyRef, tyString, tySequence: # leaf:
doOperation(cast[ppointer](d)[], op) doOperation(cast[ppointer](d)[], op)
of tyObject, tyTuple, tyPureObject: of tyObject, tyTuple, tyPureObject:
forAllSlotsAux(dest, mt.node, op) forAllSlotsAux(dest, mt.node, op)
of tyArray, tyArrayConstr, tyOpenArray:
for i in 0..(mt.size div mt.base.size)-1:
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: nil else: nil
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: TWalkOp) =
assert(cell != nil) assert(cell != nil)
assert(cell.typ != nil) assert(cell.typ != nil)
assert cell.typ.kind in {tyRef, tySequence, tyString}
case cell.typ.Kind case cell.typ.Kind
of tyRef: # common case of tyRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
@ -329,14 +330,57 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
for i in 0..s.len-1: for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +% forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
of tyString: nil else: nil
else: assert(false)
proc checkCollection {.inline.} = proc checkCollection {.inline.} =
# checks if a collection should be done # checks if a collection should be done
if recGcLock == 0: if recGcLock == 0:
collectCT(gch) collectCT(gch)
proc addNewObjToZCT(res: PCell) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down.
#
# Slots to try cache hit
# 1 32%
# 4 59%
# 8 63%
# 16 66%
# all slots 68%
var L = gch.zct.len
var d = gch.zct.d
when true:
# loop unrolled for performance:
template replaceZctEntry(i: expr) =
c = d[i]
if c.refcount >=% rcIncrement:
c.refcount = c.refcount and not colorMask
d[i] = res
return
if L > 8:
var c: PCell
replaceZctEntry(L-1)
replaceZctEntry(L-2)
replaceZctEntry(L-3)
replaceZctEntry(L-4)
replaceZctEntry(L-5)
replaceZctEntry(L-6)
replaceZctEntry(L-7)
replaceZctEntry(L-8)
add(gch.zct, res)
else:
d[L] = res
inc(gch.zct.len)
else:
for i in countdown(L-1, max(0, L-8)):
var c = d[i]
if c.refcount >=% rcIncrement:
c.refcount = c.refcount and not colorMask
d[i] = res
return
add(gch.zct, res)
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
aquire(gch) aquire(gch)
@ -354,18 +398,7 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
assert(isAllocatedPtr(allocator, res)) assert(isAllocatedPtr(allocator, res))
# its refcount is zero, so add it to the ZCT: # its refcount is zero, so add it to the ZCT:
block addToZCT: addNewObjToZCT(res)
# we check the last 8 entries (cache line) for a slot
# that could be reused
var L = gch.zct.len
var d = gch.zct.d
for i in countdown(L-1, max(0, L-8)):
var c = d[i]
if c.refcount >=% rcIncrement:
c.refcount = c.refcount and not colorMask
d[i] = res
break addToZCT
add(gch.zct, res)
when logGC: writeCell("new cell", res) when logGC: writeCell("new cell", res)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
release(gch) release(gch)

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -1,11 +1,13 @@
* thread support: threadvar on Windows seems broken;
add --deadlock_prevention:on|off switch
- implicit ref/ptr->var conversion - implicit ref/ptr->var conversion
- warning for implicit openArray -> varargs convention
- implement explicit varargs
High priority (version 0.9.0) High priority (version 0.9.0)
============================= =============================
- warning for implicit openArray -> varargs convention
- implement explicit varargs
- tests: run modules that contain "#RUN_ME", compile the other - tests: run modules that contain "#RUN_ME", compile the other
modules; run the GC tests modules; run the GC tests
- fix implicit generic routines - fix implicit generic routines
@ -32,11 +34,8 @@ To implement
* hash tables and sets; count tables; ordered dicts * hash tables and sets; count tables; ordered dicts
* distinct types for array/seq indexes * distinct types for array/seq indexes
* implement closures for the C code generator * implement closures for the C code generator
* GC: marker procs for native Nimrod GC and Boehm GC * GC: marker procs for native Nimrod GC and Boehm GC
* thread support: threadvar on Windows seems broken;
add --deadlock_prevention:on|off switch
* built-in serialization * built-in serialization