Skip to content

Commit be795bb

Browse files
authored
TlSF Alloctor: use less memory for --gc:arc (#13280)
1 parent 84e8477 commit be795bb

File tree

3 files changed

+103
-80
lines changed

3 files changed

+103
-80
lines changed

lib/system/alloc.nim

Lines changed: 100 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ type
4848
AlignType = BiggestFloat
4949
FreeCell {.final, pure.} = object
5050
next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
51-
zeroField: int # 0 means cell is not used (overlaid with typ field)
52-
# 1 means cell is manually managed pointer
53-
# otherwise a PNimType is stored in there
51+
when not defined(gcDestructors):
52+
zeroField: int # 0 means cell is not used (overlaid with typ field)
53+
# 1 means cell is manually managed pointer
54+
# otherwise a PNimType is stored in there
55+
else:
56+
alignment: int
5457

5558
PChunk = ptr BaseChunk
5659
PBigChunk = ptr BigChunk
@@ -396,8 +399,9 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} =
396399
proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
397400
magic: "Plugin", compileTime.}
398401

399-
proc isCell(p: pointer): bool {.inline.} =
400-
result = cast[ptr FreeCell](p).zeroField >% 1
402+
when not defined(gcDestructors):
403+
proc isCell(p: pointer): bool {.inline.} =
404+
result = cast[ptr FreeCell](p).zeroField >% 1
401405

402406
# ------------- chunk management ----------------------------------------------
403407
proc pageIndex(c: PChunk): int {.inline.} =
@@ -630,7 +634,8 @@ proc getSmallChunk(a: var MemRegion): PSmallChunk =
630634
result = cast[PSmallChunk](res)
631635

632636
# -----------------------------------------------------------------------------
633-
proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
637+
when not defined(gcDestructors):
638+
proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
634639

635640
when true:
636641
template allocInv(a: MemRegion): bool = true
@@ -773,7 +778,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
773778
inc(c.acc, size)
774779
else:
775780
result = c.freeList
776-
sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
781+
when not defined(gcDestructors):
782+
sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
777783
c.freeList = c.freeList.next
778784
dec(c.free, size)
779785
sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9")
@@ -826,9 +832,10 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
826832
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
827833
s == 0, "rawDealloc 3")
828834
var f = cast[ptr FreeCell](p)
829-
#echo("setting to nil: ", $cast[ByteAddress](addr(f.zeroField)))
830-
sysAssert(f.zeroField != 0, "rawDealloc 1")
831-
f.zeroField = 0
835+
when not defined(gcDestructors):
836+
#echo("setting to nil: ", $cast[ByteAddress](addr(f.zeroField)))
837+
sysAssert(f.zeroField != 0, "rawDealloc 1")
838+
f.zeroField = 0
832839
f.next = c.freeList
833840
c.freeList = f
834841
when overwriteFree:
@@ -863,88 +870,102 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
863870
sysAssert(allocInv(a), "rawDealloc: end")
864871
when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
865872

866-
proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
867-
if isAccessible(a, p):
868-
var c = pageAddr(p)
869-
if not chunkUnused(c):
870-
if isSmallChunk(c):
871-
var c = cast[PSmallChunk](c)
872-
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
873-
smallChunkOverhead()
874-
result = (c.acc >% offset) and (offset %% c.size == 0) and
875-
(cast[ptr FreeCell](p).zeroField >% 1)
876-
else:
877-
var c = cast[PBigChunk](c)
878-
result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
873+
when not defined(gcDestructors):
874+
proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
875+
if isAccessible(a, p):
876+
var c = pageAddr(p)
877+
if not chunkUnused(c):
878+
if isSmallChunk(c):
879+
var c = cast[PSmallChunk](c)
880+
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
881+
smallChunkOverhead()
882+
result = (c.acc >% offset) and (offset %% c.size == 0) and
883+
(cast[ptr FreeCell](p).zeroField >% 1)
884+
else:
885+
var c = cast[PBigChunk](c)
886+
result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
879887

880-
proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
881-
a.minLargeObj = lowGauge(a.root)
882-
a.maxLargeObj = highGauge(a.root)
888+
proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
889+
a.minLargeObj = lowGauge(a.root)
890+
a.maxLargeObj = highGauge(a.root)
883891

884-
proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
885-
if isAccessible(a, p):
886-
var c = pageAddr(p)
887-
if not chunkUnused(c):
888-
if isSmallChunk(c):
889-
var c = cast[PSmallChunk](c)
890-
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
891-
smallChunkOverhead()
892-
if c.acc >% offset:
893-
sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
894-
cast[ByteAddress](p), "offset is not what you think it is")
895-
var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
896-
offset -% (offset %% c.size))
897-
if d.zeroField >% 1:
892+
proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
893+
if isAccessible(a, p):
894+
var c = pageAddr(p)
895+
if not chunkUnused(c):
896+
if isSmallChunk(c):
897+
var c = cast[PSmallChunk](c)
898+
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
899+
smallChunkOverhead()
900+
if c.acc >% offset:
901+
sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
902+
cast[ByteAddress](p), "offset is not what you think it is")
903+
var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
904+
offset -% (offset %% c.size))
905+
if d.zeroField >% 1:
906+
result = d
907+
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
908+
else:
909+
var c = cast[PBigChunk](c)
910+
var d = addr(c.data)
911+
if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
898912
result = d
899913
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
900-
else:
901-
var c = cast[PBigChunk](c)
902-
var d = addr(c.data)
903-
if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
904-
result = d
905-
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
906-
else:
907-
var q = cast[int](p)
908-
if q >=% a.minLargeObj and q <=% a.maxLargeObj:
909-
# this check is highly effective! Test fails for 99,96% of all checks on
910-
# an x86-64.
911-
var avlNode = inRange(a.root, q)
912-
if avlNode != nil:
913-
var k = cast[pointer](avlNode.key)
914-
var c = cast[PBigChunk](pageAddr(k))
915-
sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
916-
if cast[ptr FreeCell](k).zeroField >% 1:
917-
result = k
918-
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
914+
else:
915+
var q = cast[int](p)
916+
if q >=% a.minLargeObj and q <=% a.maxLargeObj:
917+
# this check is highly effective! Test fails for 99,96% of all checks on
918+
# an x86-64.
919+
var avlNode = inRange(a.root, q)
920+
if avlNode != nil:
921+
var k = cast[pointer](avlNode.key)
922+
var c = cast[PBigChunk](pageAddr(k))
923+
sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
924+
if cast[ptr FreeCell](k).zeroField >% 1:
925+
result = k
926+
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
919927

920928
proc ptrSize(p: pointer): int =
921-
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
922-
var c = pageAddr(p)
923-
sysAssert(not chunkUnused(c), "ptrSize")
924-
result = c.size -% sizeof(FreeCell)
925-
if not isSmallChunk(c):
926-
dec result, bigChunkOverhead()
929+
when not defined(gcDestructors):
930+
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
931+
var c = pageAddr(p)
932+
sysAssert(not chunkUnused(c), "ptrSize")
933+
result = c.size -% sizeof(FreeCell)
934+
if not isSmallChunk(c):
935+
dec result, bigChunkOverhead()
936+
else:
937+
var c = pageAddr(p)
938+
sysAssert(not chunkUnused(c), "ptrSize")
939+
result = c.size
940+
if not isSmallChunk(c):
941+
dec result, bigChunkOverhead()
927942

928943
proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} =
929-
result = rawAlloc(allocator, size+sizeof(FreeCell))
930-
cast[ptr FreeCell](result).zeroField = 1 # mark it as used
931-
sysAssert(not isAllocatedPtr(allocator, result), "alloc")
932-
result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
933-
track("alloc", result, size)
944+
when not defined(gcDestructors):
945+
result = rawAlloc(allocator, size+sizeof(FreeCell))
946+
cast[ptr FreeCell](result).zeroField = 1 # mark it as used
947+
sysAssert(not isAllocatedPtr(allocator, result), "alloc")
948+
result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
949+
track("alloc", result, size)
950+
else:
951+
result = rawAlloc(allocator, size)
934952

935953
proc alloc0(allocator: var MemRegion, size: Natural): pointer =
936954
result = alloc(allocator, size)
937955
zeroMem(result, size)
938956

939957
proc dealloc(allocator: var MemRegion, p: pointer) =
940-
sysAssert(p != nil, "dealloc: p is nil")
941-
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
942-
sysAssert(x != nil, "dealloc: x is nil")
943-
sysAssert(isAccessible(allocator, x), "is not accessible")
944-
sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
945-
rawDealloc(allocator, x)
946-
sysAssert(not isAllocatedPtr(allocator, x), "dealloc: object still accessible")
947-
track("dealloc", p, 0)
958+
when not defined(gcDestructors):
959+
sysAssert(p != nil, "dealloc: p is nil")
960+
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
961+
sysAssert(x != nil, "dealloc: x is nil")
962+
sysAssert(isAccessible(allocator, x), "is not accessible")
963+
sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
964+
rawDealloc(allocator, x)
965+
sysAssert(not isAllocatedPtr(allocator, x), "dealloc: object still accessible")
966+
track("dealloc", p, 0)
967+
else:
968+
rawDealloc(allocator, p)
948969

949970
proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
950971
if newsize > 0:
@@ -958,7 +979,7 @@ proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
958979
proc realloc0(allocator: var MemRegion, p: pointer, oldsize, newsize: Natural): pointer =
959980
result = realloc(allocator, p, newsize)
960981
if newsize > oldsize:
961-
zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize)
982+
zeroMem(cast[pointer](cast[uint](result) + uint(oldsize)), newsize - oldsize)
962983

963984
proc deallocOsPages(a: var MemRegion) =
964985
# we free every 'ordinarily' allocated page by iterating over the page bits:

lib/system/strs_v2.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
122122
a.len = b.len
123123
a.p = b.p
124124
else:
125-
if isLiteral(a) or (a.p.cap and not strlitFlag) < b.len:
125+
if isLiteral(a) or (a.p.cap and not strlitFlag) < b.len:
126126
# we have to allocate the 'cap' here, consider
127127
# 'let y = newStringOfCap(); var x = y'
128128
# on the other hand... These get turned into moves now.

tests/gc/gcbench.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ proc main() =
167167
var elapsed = epochTime() - t
168168
printDiagnostics()
169169
echo("Completed in " & $elapsed & "s. Success!")
170+
when declared(getMaxMem):
171+
echo "Max memory ", formatSize getMaxMem()
170172

171173
when defined(GC_setMaxPause):
172174
GC_setMaxPause 2_000

0 commit comments

Comments
 (0)