Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions packaging/dependencies.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,10 @@ scope: {
inherit stdenv;
}).overrideAttrs
(attrs: {
# Reduce contention on the GC allocation lock during parallel
# evaluation by handing out multiple heap blocks worth of
# objects per lock acquisition in GC_generic_malloc_many().
# The default batch size is set via GC_MANY_BLOCKS_DEFAULT
# below and can be overridden at runtime through the
# GC_MALLOC_MANY_BLOCKS environment variable.
patches = (attrs.patches or [ ]) ++ [ ./patches/boehmgc-batch-malloc-many.patch ];
patches = (attrs.patches or [ ]) ++ [
./patches/boehmgc-batch-malloc-many.patch
./patches/boehmgc-gctest-tiny-freelists-heap-growth.patch
];

env = (attrs.env or { }) // {
# Increase the initial mark stack size to avoid stack
Expand All @@ -55,6 +52,14 @@ scope: {
[
"-DINITIAL_MARK_STACK_SIZE=1048576"
"-DGC_MANY_BLOCKS_DEFAULT=64"
# Serve allocations up to 1520 bytes (95 granules) from
# the per-thread freelists instead of taking the global
# allocation lock. The default (25, i.e. <= 384 bytes) is
# too small for parallel evaluation: e.g. a typical
# derivation attrset (~46 attrs) is a 752-byte Bindings,
# of which nixpkgs evaluation does hundreds of thousands,
# all serialized on GC_allocate_ml.
"-DGC_TINY_FREELISTS=96"
]
# For some reason that is not clear, it is wanting to use libgcc_eh which is not available.
# Force this to be built with compiler-rt & libunwind over libgcc_eh works.
Expand Down
38 changes: 38 additions & 0 deletions packaging/patches/boehmgc-gctest-tiny-freelists-heap-growth.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
From 7cd4f090acc0e801502f918060106bba926c7153 Mon Sep 17 00:00:00 2001
From: Eelco Dolstra <edolstra@gmail.com>
Date: Tue, 25 Aug 2026 14:37:18 +0200
Subject: [PATCH] Scale gctest's heap growth limit with GC_TINY_FREELISTS

The 'unexpected heap growth' check in check_heap_stats() uses a fixed
limit that is exceeded when the collector is built with a larger
GC_TINY_FREELISTS: bigger thread-local free lists cause each thread to
retain more nearly-empty heap blocks (up to one per size class and
object kind) and increase block-level fragmentation. Scale the limit
proportionally so that gctest passes with such configurations.

Assisted-by: Claude Fable 5 <noreply@anthropic.com>
---
tests/test.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/tests/test.c b/tests/test.c
index 0ae3f95b..b62699fa 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -1753,6 +1753,13 @@ void check_heap_stats(void)
# endif
# ifdef MEMORY_SANITIZER
max_heap_sz += max_heap_sz / 4;
+# endif
+# if defined(GC_TINY_FREELISTS) && GC_TINY_FREELISTS > 25
+ /* Larger thread-local free lists cause each thread to retain */
+ /* more nearly-empty heap blocks (up to one per size class and */
+ /* object kind) and increase block-level fragmentation, so */
+ /* allow the heap to grow proportionally. */
+ max_heap_sz = max_heap_sz / 25 * GC_TINY_FREELISTS;
# endif
max_heap_sz *= n_tests;
# if defined(USE_MMAP) || defined(MSWIN32)
--
2.54.0

Loading