wingo pushed a commit to branch wip-whippet
in repository guile.

commit 43ccb460ad4545a170e3e71730d165e506adb1b0
Author: Andy Wingo <wi...@igalia.com>
AuthorDate: Thu Aug 7 11:39:43 2025 +0200

    bdw: Use heap sizer instead of BDW's own heuristics
---
 Makefile  |  2 +-
 embed.mk  |  2 +-
 src/bdw.c | 56 +++++++++++++++++++++++++++++++++++++++-----------------
 3 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/Makefile b/Makefile
index ccac277ee..cfed203d7 100644
--- a/Makefile
+++ b/Makefile
@@ -75,7 +75,7 @@ obj/%.gc-finalizer.o: src/gc-finalizer.c | .deps obj
 GC_STEM_bdw       = bdw
 GC_CPPFLAGS_bdw    = -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1
 GC_IMPL_CFLAGS_bdw = `pkg-config --cflags bdw-gc`
-GC_LIBS_bdw        = `pkg-config --libs bdw-gc`
+GC_LIBS_bdw        = `pkg-config --libs bdw-gc` -lm
 
 GC_STEM_semi       = semi
 GC_CPPFLAGS_semi   = -DGC_PRECISE_ROOTS=1
diff --git a/embed.mk b/embed.mk
index e8967efca..71e0997f0 100644
--- a/embed.mk
+++ b/embed.mk
@@ -55,7 +55,7 @@ $(GC_OBJDIR)gc-finalizer.o: $(GC_BASE)src/gc-finalizer.c
 GC_STEM_bdw       = bdw
 GC_CPPFLAGS_bdw    = -DGC_CONSERVATIVE_ROOTS=1 -DGC_CONSERVATIVE_TRACE=1
 GC_IMPL_CFLAGS_bdw = `pkg-config --cflags bdw-gc`
-GC_LIBS_bdw        = `pkg-config --libs bdw-gc`
+GC_LIBS_bdw        = `pkg-config --libs bdw-gc` -lm
 
 GC_STEM_semi       = semi
 GC_CPPFLAGS_semi   = -DGC_PRECISE_ROOTS=1
diff --git a/src/bdw.c b/src/bdw.c
index 7709b16de..aa81a54c7 100644
--- a/src/bdw.c
+++ b/src/bdw.c
@@ -12,6 +12,7 @@
 #include "gc-tracepoint.h"
 
 #include "gc-internal.h"
+#include "heap-sizer.h"
 
 #include "bdw-attrs.h"
 
@@ -59,6 +60,7 @@ struct gc_heap {
   struct gc_event_listener event_listener;
   struct gc_finalizer_state *finalizer_state;
   gc_finalizer_callback have_finalizers;
+  struct gc_heap_sizer sizer;
   void *event_listener_data;
   void* (*allocation_failure)(struct gc_heap *, size_t);
 };
@@ -126,8 +128,20 @@ allocate_small(void **freelist, size_t idx, enum 
gc_inline_kind kind) {
   return head;
 }
 
+static size_t gc_pending_heap_size;
+
 void* gc_allocate_slow(struct gc_mutator *mut, size_t size,
                        enum gc_allocation_kind kind) {
+  if (GC_UNLIKELY(gc_pending_heap_size)) {
+    size_t new_heap_size = gc_pending_heap_size;
+    gc_pending_heap_size = 0;
+    size_t current = GC_get_heap_size();
+    if (current < new_heap_size) {
+      GC_expand_hp(new_heap_size - current);
+      HEAP_EVENT(heap_resized, new_heap_size);
+    }
+  }
+
   GC_ASSERT(size != 0);
   if (size <= gc_allocator_large_threshold()) {
     switch (kind) {
@@ -521,9 +535,16 @@ gc_heap_pending_ephemerons(struct gc_heap *heap) {
   return NULL;
 }
 
+static uint64_t gc_start_ns;
+
+static void resize_heap(struct gc_heap *heap, size_t new_heap_size) {
+  gc_pending_heap_size = new_heap_size;
+}
+
 static void on_collection_event(GC_EventType event) {
   switch (event) {
   case GC_EVENT_START: {
+    gc_start_ns = gc_platform_monotonic_nanoseconds();
     HEAP_EVENT(requesting_stop);
     HEAP_EVENT(waiting_for_stop);
     break;
@@ -542,11 +563,18 @@ static void on_collection_event(GC_EventType event) {
     // Sloppily attribute finalizers and eager reclamation to
     // ephemerons.
     HEAP_EVENT(ephemerons_traced);
-    // FIXME: This overestimates the live data size, as blocks that have at
-    // least one live object will be lazily swept, and free space discovered in
-    // those objects will be added to GC_bytes_found, which would need to be
-    // subtracted from this value.
-    HEAP_EVENT(live_data_size, GC_get_heap_size() - GC_get_free_bytes());
+    {
+      size_t heap_size = GC_get_heap_size();
+      // FIXME: This overestimates the live data size, as blocks that have at
+      // least one live object will be lazily swept, and free space discovered 
in
+      // those objects will be added to GC_bytes_found, which would need to be
+      // subtracted from this value.
+      size_t live_data_size = heap_size - GC_get_free_bytes();
+      uint64_t pause_ns = gc_platform_monotonic_nanoseconds() - gc_start_ns;
+      HEAP_EVENT(live_data_size, live_data_size);
+      gc_heap_sizer_on_gc(__the_bdw_gc_heap->sizer, heap_size, live_data_size,
+                          pause_ns, resize_heap);
+    }
     break;
   case GC_EVENT_END:
     HEAP_EVENT(restarting_mutators);
@@ -616,18 +644,8 @@ int gc_init(const struct gc_options *options, struct 
gc_stack_addr stack_base,
     case GC_HEAP_SIZE_GROWABLE: {
       if (options->common.maximum_heap_size)
         GC_set_max_heap_size(options->common.maximum_heap_size);
-      // BDW uses a pretty weird heap-sizing heuristic:
-      //
-      // heap-size = live-data * (1 + (2 / GC_free_space_divisor))
-      // heap-size-multiplier = heap-size/live-data = 1 + 
2/GC_free_space_divisor
-      // GC_free_space_divisor = 2/(heap-size-multiplier-1)
-      //
-      // (Assumption: your heap is mostly "composite", i.e. not
-      // "atomic".  See bdw's alloc.c:min_bytes_allocd.)
-      double fsd = 2.0/(options->common.heap_size_multiplier - 1);
-      // But, the divisor is an integer.  WTF.  This caps the effective
-      // maximum heap multiplier at 3.  Oh well.
-      GC_set_free_space_divisor(fsd + 0.51);
+      // BDW has other odd heap-sizing heuristics, but we mostly replace them
+      // via our use of the heap sizer.
       break;
     }
     case GC_HEAP_SIZE_ADAPTIVE:
@@ -683,6 +701,10 @@ int gc_init(const struct gc_options *options, struct 
gc_stack_addr stack_base,
   GC_set_oom_fn (oom_fn);
   (*heap)->allocation_failure = allocation_failure;
 
+  (*heap)->sizer = gc_make_heap_sizer(*heap, &options->common,
+                                      gc_allocation_counter, resize_heap,
+                                      NULL);
+
   *mutator = add_mutator(*heap);
 
   // Sanity check.

Reply via email to