From 7ee4b42929c94d1ba86a45315c1f655da42c0349 Mon Sep 17 00:00:00 2001 From: araq Date: Thu, 2 Apr 2026 10:16:41 +0200 Subject: [PATCH] also optimize memory consumption for mm:orc --- lib/system/alloc.nim | 11 +++++++++++ lib/system/arc.nim | 30 +++++++++++++++++++++++------- tests/align/talign_heap.nim | 23 +++++++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 tests/align/talign_heap.nim diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 9c2d5c69dd..2257de09f1 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -1265,6 +1265,17 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = proc alloc0Impl(size: Natural): pointer = result = alloc0(allocator, size) + when defined(gcOrc) or defined(gcYrc): + proc nimAlignedAlloc0(size: Natural, alignment: int): pointer = + result = rawAlloc(allocator, size, alignment) + zeroMem(result, size) + + proc nimAlignedAlloc(size: Natural, alignment: int): pointer = + result = rawAlloc(allocator, size, alignment) + + proc nimAlignedDealloc(p: pointer) = + rawDealloc(allocator, p) + proc deallocImpl(p: pointer) = dealloc(allocator, p) diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 3ac84be3bb..ced096059e 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -92,12 +92,23 @@ else: when not defined(nimHasQuirky): {.pragma: quirky.} +# Forward declarations for native allocator alignment (implemented in alloc.nim). +# rawAlloc's contract: result + sizeof(FreeCell) is alignment-aligned. +# For ORC/YRC, sizeof(FreeCell) == sizeof(RefHeader). +when (defined(gcOrc) or defined(gcYrc)) and not defined(useMalloc) and not defined(nimscript): + proc nimAlignedAlloc0(size: Natural, alignment: int): pointer {.gcsafe, raises: [].} + proc nimAlignedAlloc(size: Natural, alignment: int): pointer {.gcsafe, raises: [].} + proc nimAlignedDealloc(p: pointer) {.gcsafe, raises: [].} + proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} = - let hdrSize = align(sizeof(RefHeader), alignment) - let s = size +% hdrSize when defined(nimscript): discard + elif (defined(gcOrc) or defined(gcYrc)) and not defined(useMalloc): + let s = size +% sizeof(RefHeader) + result = nimAlignedAlloc0(s, alignment) +! sizeof(RefHeader) else: + let hdrSize = align(sizeof(RefHeader), alignment) + let s = size +% hdrSize result = alignedAlloc0(s, alignment) +! hdrSize when defined(nimArcDebug) or defined(nimArcIds): head(result).refId = gRefId @@ -111,12 +122,14 @@ proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} = proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} = # Same as 'newNewObj' but do not initialize the memory to zero. - # The codegen proved for us that this is not necessary. - let hdrSize = align(sizeof(RefHeader), alignment) - let s = size + hdrSize when defined(nimscript): discard + elif (defined(gcOrc) or defined(gcYrc)) and not defined(useMalloc): + let s = size + sizeof(RefHeader) + result = cast[ptr RefHeader](nimAlignedAlloc(s, alignment) +! sizeof(RefHeader)) else: + let hdrSize = align(sizeof(RefHeader), alignment) + let s = size + hdrSize result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize) head(result).rc = 0 when defined(gcOrc) or defined(gcYrc): @@ -189,8 +202,11 @@ proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} = if freedCells.data == nil: init(freedCells) freedCells.incl head(p) else: - let hdrSize = align(sizeof(RefHeader), alignment) - alignedDealloc(p -! hdrSize, alignment) + when (defined(gcOrc) or defined(gcYrc)) and not defined(useMalloc): + nimAlignedDealloc(p -! sizeof(RefHeader)) + else: + let hdrSize = align(sizeof(RefHeader), alignment) + alignedDealloc(p -! hdrSize, alignment) template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf) #proc dispose*(x: pointer) = nimRawDispose(x) diff --git a/tests/align/talign_heap.nim b/tests/align/talign_heap.nim new file mode 100644 index 0000000000..65abf728ac --- /dev/null +++ b/tests/align/talign_heap.nim @@ -0,0 +1,23 @@ +discard """ + matrix: "--mm:refc; --mm:orc; --mm:arc" + targets: "c cpp" + output: "ok" +""" + +# Test that heap-allocated objects with .align use small chunks, +# not a big chunk per object (regression test for #25577). +type U = object + d {.align: 32.}: int8 + +var e: seq[ref U] +for _ in 0 ..< 10000: e.add(new U) + +# Without small-chunk alignment, each object gets its own page (~46 MB). +# With the fix, 10000 objects fit in ~1-3 MB depending on the GC. +doAssert getTotalMem() < 8 * 1024 * 1024, "align:32 heap objects use too much memory" + +# Verify alignment is actually correct +for i in 0 ..< e.len: + doAssert (cast[int](addr e[i].d) and 31) == 0, "field not 32-byte aligned" + +echo "ok"