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 (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM):
appf(generatedProc,
"profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n",
[toRope(prc.loc.a)])
"profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n",
[toRope(prc.loc.a)])
app(generatedProc, returnStmt)
app(generatedProc, '}' & tnl)
app(m.s[cfsProcs], generatedProc)

View file

@ -100,19 +100,16 @@ proc crcFromBuf(buf: Pointer, length: int): TCrc32 =
proc crcFromFile(filename: string): TCrc32 =
const
bufSize = 8 * 1024
bufSize = 8000 # don't use 8K for the memory allocator!
var
bin: tfile
buf: Pointer
readBytes: int
p: PByteArray
result = InitCrc32
if not open(bin, filename):
return # not equal if file does not exist
buf = alloc(BufSize)
p = cast[PByteArray](buf)
var buf = alloc(BufSize)
var p = cast[PByteArray](buf)
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)
if readBytes != bufSize: break
dealloc(buf)

View file

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

View file

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

View file

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

View file

@ -100,7 +100,7 @@ when defined(Windows):
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
proc WaitForSingleObject(hHandle: THANDLE, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
proc TerminateThread(hThread: THandle, dwExitCode: int32): int32 {.
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)
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)
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
dec(t.counter)

View file

@ -23,28 +23,39 @@ proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline.}
# Support for thread local storage:
when false:
when defined(windows):
proc TlsAlloc(): int32 {.importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
proc TlsSetValue(dwTlsIndex: int32, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc TlsGetValue(dwTlsIndex: int32): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
when defined(windows):
type
TThreadVarSlot {.compilerproc.} = distinct int32
else:
type
Tpthread_key {.importc: "pthread_key_t", header: "<sys/types.h>".} = int
proc TlsAlloc(): TThreadVarSlot {.
importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
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 {.
importc: "pthread_getspecific", header: "<pthread.h>".}
proc pthread_key_create(a1: ptr Tpthread_key,
a2: 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 ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} =
result = TlsAlloc()
proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.compilerproc.} =
TlsSetValue(s, value)
proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc.} =
result = TlsGetValue(s)
proc pthread_setspecific(a1: Tpthread_key, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".}
else:
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
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -21,8 +21,6 @@ proc rawWrite(f: TFile, s: string) =
proc nimLoadLibraryError(path: string) =
# carefully written to avoid memory allocation:
#stdout.write("could not load: ")
#quit(path)
stdout.rawWrite("could not load: ")
stdout.rawWrite(path)
stdout.rawWrite("\n")

View file

@ -23,7 +23,7 @@
const
CycleIncrease = 2 # is a multiplicative increase
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
# this seems to be a good value
@ -294,31 +294,32 @@ proc initGC() =
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
var d = cast[TAddress](dest)
case n.kind
of nkNone: assert(false)
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
of nkList:
for i in 0..n.len-1: forAllSlotsAux(dest, n.sons[i], op)
of nkCase:
var m = selectBranch(dest, n)
if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: assert(false)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
var d = cast[TAddress](dest)
if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags:
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:
doOperation(cast[ppointer](d)[], op)
of tyObject, tyTuple, tyPureObject:
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
proc forAllChildren(cell: PCell, op: TWalkOp) =
assert(cell != nil)
assert(cell.typ != nil)
assert cell.typ.kind in {tyRef, tySequence, tyString}
case cell.typ.Kind
of tyRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
@ -329,14 +330,57 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
of tyString: nil
else: assert(false)
else: nil
proc checkCollection {.inline.} =
# checks if a collection should be done
if recGcLock == 0:
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.} =
# generates a new object and sets its reference counter to 0
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
assert(isAllocatedPtr(allocator, res))
# its refcount is zero, so add it to the ZCT:
block addToZCT:
# 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)
addNewObjToZCT(res)
when logGC: writeCell("new cell", res)
gcTrace(res, csAllocated)
release(gch)

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# 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
- warning for implicit openArray -> varargs convention
- implement explicit varargs
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
modules; run the GC tests
- fix implicit generic routines
@ -32,11 +34,8 @@ To implement
* hash tables and sets; count tables; ordered dicts
* distinct types for array/seq indexes
* implement closures for the C code generator
* 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