summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylan Kammer <taylan.kammer@gmail.com>2026-09-22 08:14:03 +0200
committerTaylan Kammer <taylan.kammer@gmail.com>2026-09-22 08:15:54 +0200
commit470bcb1b7a34c5eb95c95f1b01343740d459d42c (patch)
tree251498c5420124fb13b772547e0b9d89af88c10b
parent685a7b3456185c39e7813c3c69ab0b346daf9e1f (diff)
Amend latest note again.
-rw-r--r--notes/260922-use-alloc-3.md58
1 files changed, 58 insertions, 0 deletions
diff --git a/notes/260922-use-alloc-3.md b/notes/260922-use-alloc-3.md
index abe520d..fc87293 100644
--- a/notes/260922-use-alloc-3.md
+++ b/notes/260922-use-alloc-3.md
@@ -1,9 +1,12 @@
# Using Meta Alloc, Attempt 3
+<!--TOC-->
+
_2026 September_
Here's a fairly concrete design proposal.
+
## Block Alloc
This is a simple manual alloc/free allocator, based on the Meta Alloc
@@ -27,6 +30,7 @@ the Meta Alloc limitation of 256 GiB total heap / 16 GiB per slab.
(Every four size classes, roughly; first few classes being exceptions
e.g. 8-byte size class gets an entire slab for itself.)
+
## GC Alloc
A fully concurrent (virtually "zero pause") mark-sweep tracing GC
@@ -97,6 +101,8 @@ barriers with colored pointers and whatnot, like ZGC. It's unclear
whether the CPU overhead would be justified; all it gives you is
potentially improved spatial locality of objects in memory.
+### Pinning
+
Oh, and with regards to object pinning, Gemini gave me a really cool
idea on how to do it efficiently:
@@ -133,3 +139,55 @@ and unfortunately there's three of them, but that still gives us:
(3 * 640 MiB) / 256 GiB ~= 0.73% metadata overhead
That's quite good I think.
+
+### Explicit free
+
+As an optimization trick offered to application code that otherwise
+uses GC, there could be a special explicit `gc_free()` function.
+
+This would clear the GC occupancy bit for the given memory slot, and
+place it into the thread-local allocation buffer. (The same that's
+fed from the global shared free-list for a size class.)
+
+Clearing the occupancy bit ensures that the sweep phase doesn't touch
+it and the block it's placed in can't be freed to Meta Alloc. It's
+basically as if it had been swept by the GC, but lands immediately
+within the thread-local alloc buffer.
+
+If the local buffer is full, I suppose a chunk of it would be flushed
+into the shared free-list of the size class. Basically the same as
+Block Alloc and Meta Alloc. That's somewhat costlier than simply
+putting it into the local buffer, but should still remain under a
+microsecond I suppose.
+
+This way, an application that allocates and frees a bunch of objects
+in a tight loop could make sure that it keeps reusing the same few
+memory slots already in L1D cache.
+
+If the allocation happens at the start of the loop and the freeing at
+the end, then the programmer could have just used manual alloc/free,
+or used stack memory, and a smart optimizer should even detect this
+pattern and turn it into stack usage anyway...
+
+But if the loop crosses across module boundaries, conditionals are
+involved, or it frees some old objects at the start of the loop and
+then creates new ones or creates them at the end, I could see some
+situations where this is a useful API to offer.
+
+(Conditional free is probably the strongest case; in all other cases
+the object didn't need to be created with `gc_alloc()` to begin with,
+I think. Haven't thought hard enough about it.)
+
+### GC handover
+
+An object initially allocated as a manually managed object could be
+simply handed over to the GC, if there is some logical reason to do
+such a thing.
+
+All this entails is setting the GC occupancy bit for the memory slot.
+That's literally all; the object is now GC managed.
+
+Of course, if the object acts as the root of a whole graph of objects
+that were manually allocated and are meant to be handed over to GC,
+then a traversal of that graph may be necessary to hand all of them
+over to GC.