1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-08 22:50:27 +02:00
Mirror of the upstream GNU Guile repository on Savannah. https://www.gnu.org/software/guile/
Find a file
2023-08-06 10:43:42 +02:00
address-hash.h Add address map and set 2022-04-12 21:41:26 +02:00
address-map.h Add address map and set 2022-04-12 21:41:26 +02:00
address-set.h Add large object space 2022-04-13 21:43:18 +02:00
assert.h Add large object space to mark-sweep collector 2022-04-18 21:20:00 +02:00
bdw-attrs.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
bdw.c Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
debug.h Switch mark-sweep collector to mark stack 2022-03-11 11:23:58 +01:00
ephemerons-embedder.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
ephemerons-types.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
ephemerons.c ephemerons benchmark takes gc options 2023-02-28 11:30:51 +01:00
gc-align.h Add conservative stack capture 2022-10-03 16:09:21 +02:00
gc-api.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
gc-assert.h Separate compilation!!!!! 2022-08-16 17:54:15 +02:00
gc-attrs.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
gc-config.h Add conservative heap tracing (not just roots) 2022-10-26 10:37:55 +02:00
gc-conservative-ref.h Whippet can trace conservative roots 2022-10-03 16:09:21 +02:00
gc-edge.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
gc-embedder-api.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
gc-ephemeron-internal.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
gc-ephemeron.c Add ephemeron implementation 2023-01-23 20:56:25 +01:00
gc-ephemeron.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
gc-forwarding.h More typesafety, more gc_ref 2022-08-16 22:48:46 +02:00
gc-inline.h Separate tagging from collector 2022-08-12 16:44:38 +02:00
gc-internal.h Rework options interface 2023-02-15 20:07:14 +01:00
gc-options-internal.h Rework options interface 2023-02-15 20:07:14 +01:00
gc-options.c Add enum heap policy parsing 2023-02-28 11:19:42 +01:00
gc-options.h Rework options interface 2023-02-15 20:07:14 +01:00
gc-platform-gnu-linux.c Rework options interface 2023-02-15 20:07:14 +01:00
gc-platform.h Rework options interface 2023-02-15 20:07:14 +01:00
gc-ref.h Separate tagging from collector 2022-08-12 16:44:38 +02:00
gc-stack.c Pass heap to tracer functions 2022-10-25 14:25:55 +02:00
gc-stack.h Pass heap to tracer functions 2022-10-25 14:25:55 +02:00
gc-trace.h Refactor embedder interface for conservative GC 2022-10-26 11:59:56 +02:00
gc-visibility.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
gc.h Rename gc-types.h to gc-api.h 2022-08-08 11:08:36 +02:00
heap-objects.h Separate out embedder API from mt-gcbench, quads 2022-08-16 16:09:36 +02:00
large-object-space.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
Makefile Rework options interface 2023-02-15 20:07:14 +01:00
mt-gcbench-embedder.h Add conservative heap tracing (not just roots) 2022-10-26 10:37:55 +02:00
mt-gcbench-types.h Separate compilation!!!!! 2022-08-16 17:54:15 +02:00
mt-gcbench.c Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
parallel-tracer.h Add conservative heap tracing (not just roots) 2022-10-26 10:37:55 +02:00
quads-embedder.h Pass heap to tracer functions 2022-10-25 14:25:55 +02:00
quads-types.h Separate out embedder API from mt-gcbench, quads 2022-08-16 16:09:36 +02:00
quads.c quads benchmark takes gc-options param 2023-02-28 11:24:33 +01:00
README.md Update README 2023-01-23 20:57:02 +01:00
semi-attrs.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
semi.c Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
serial-tracer.h Add conservative heap tracing (not just roots) 2022-10-26 10:37:55 +02:00
simple-allocator.h More typesafety, more gc_ref 2022-08-16 22:48:46 +02:00
simple-gc-embedder.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
simple-roots-api.h Add ephemeron implementation 2023-01-23 20:56:25 +01:00
simple-roots-types.h Refactor embedder interface for conservative GC 2022-10-26 11:59:56 +02:00
simple-tagging-scheme.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
spin.h Add helper for yielding in a spinlock 2022-07-20 14:40:47 +02:00
test-address-map.c Add address map and set 2022-04-12 21:41:26 +02:00
test-address-set.c Add address map and set 2022-04-12 21:41:26 +02:00
USER-GUIDE.md Add USER-GUIDE.md 2023-08-06 10:43:42 +02:00
whippet-attrs.h Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00
whippet.c Allow large object space to be part of remembered set 2023-03-15 09:34:12 +01:00

Whippet Garbage Collector

This repository is for development of Whippet, a new garbage collector implementation, eventually for use in Guile Scheme.

Design

Whippet is mainly a mark-region collector, like Immix. See also the lovely detailed Rust implementation.

To a first approximation, Whippet is a whole-heap Immix collector with a large object space on the side. See the Immix paper for full details, but basically Immix divides the heap into 32kB blocks, and then divides those blocks into 128B lines. An Immix allocation never spans blocks; allocations larger than 8kB go into a separate large object space. Mutators request blocks from the global store and allocate into those blocks using bump-pointer allocation. When all blocks are consumed, Immix stops the world and traces the object graph, marking objects but also the lines that objects are on. After marking, blocks contain some lines with live objects and others that are completely free. Spans of free lines are called holes. When a mutator gets a recycled block from the global block store, it allocates into those holes. Also, sometimes Immix can choose to evacuate rather than mark. Bump-pointer-into-holes allocation is quite compatible with conservative roots, so it's an interesting option for Guile, which has a lot of legacy C API users.

The essential difference of Whippet from Immix stems from a simple observation: Immix needs a side table of line mark bytes and also a mark bit or bits in each object (or in a side table). But if instead you choose to store mark bytes instead of bits (for concurrency reasons) in a side table, with one mark byte per granule (unit of allocation, perhaps 16 bytes), then you effectively have a line mark table where the granule size is the line size. You can bump-pointer allocate into holes in the mark byte table.

You might think this is a bad tradeoff, and perhaps it is: I don't know yet. If your granule size is two pointers, then one mark byte per granule is 6.25% overhead on 64-bit, or 12.5% on 32-bit. Especially on 32-bit, it's a lot! On the other hand, instead of the worst case of one survivor object wasting a line (or two, in the case of conservative line marking), granule-size-is-line-size instead wastes nothing. Also, you don't need GC bits in the object itself, and you can use the mark byte array to record the object end, so that finding holes in a block can just read the mark table and can avoid looking at object memory.

Other ideas in Whippet:

  • Minimize stop-the-world phase via parallel marking and punting all sweeping to mutators

  • Enable mutator parallelism via lock-free block acquisition and lazy statistics collation

  • Allocate block space using aligned 4 MB slabs, with embedded metadata to allow metadata bytes, slab headers, and block metadata to be located via address arithmetic

  • Facilitate conservative collection via mark byte array, oracle for "does this address start an object"

  • Enable in-place generational collection via card table with one entry per 256B or so

  • Enable concurrent marking by having three mark bit states (dead, survivor, marked) that rotate at each collection, and sweeping a block clears metadata for dead objects; but concurrent marking and associated SATB barrier not yet implemented

What's there

This repository is a workspace for Whippet implementation. As such, it has files implementing Whippet itself. It also has some benchmarks to use in optimizing Whippet:

  • mt-gcbench.c: The multi-threaded GCBench benchmark. An old but standard benchmark that allocates different sizes of binary trees. As parameters it takes a heap multiplier and a number of mutator threads. We analytically compute the peak amount of live data and then size the GC heap as a multiplier of that size. It has a peak heap consumption of 10 MB or so per mutator thread: not very large. At a 2x heap multiplier, it causes about 30 collections for the whippet collector, and runs somewhere around 200-400 milliseconds in single-threaded mode, on the machines I have in 2022. For low thread counts, the GCBench benchmark is small; but then again many Guile processes also are quite short-lived, so perhaps it is useful to ensure that small heaps remain lightweight.

    To stress Whippet's handling of fragmentation, we modified this benchmark to intersperse pseudorandomly-sized holes between tree nodes.

  • quads.c: A synthetic benchmark that allocates quad trees. The mutator begins by allocating one long-lived tree of depth N, and then allocates 13% of the heap in depth-3 trees, 20 times, simulating a fixed working set and otherwise an allocation-heavy workload. By observing the times to allocate 13% of the heap in garbage we can infer mutator overheads, and also note the variance for the cycles in which GC hits.

The repository has two other collector implementations, to appropriately situate Whippet's performance in context:

  • bdw.h: The external BDW-GC conservative parallel stop-the-world mark-sweep segregated-fits collector with lazy sweeping.
  • semi.h: Semispace copying collector.
  • whippet.h: The whippet collector. Two different marking implementations: single-threaded and parallel. Generational and non-generational variants, also.

Guile

If the Whippet collector works out, it could replace Guile's garbage collector. Guile currently uses BDW-GC. Guile has a widely used C API and implements part of its run-time in C. For this reason it may be infeasible to require precise enumeration of GC roots -- we may need to allow GC roots to be conservatively identified from data sections and from stacks. Such conservative roots would be pinned, but other objects can be moved by the collector if it chooses to do so. We assume that object references within a heap object can be precisely identified. (However, Guile currently uses BDW-GC in its default configuration, which scans for references conservatively even on the heap.)

The existing C API allows direct access to mutable object fields, without the mediation of read or write barriers. Therefore it may be impossible to switch to collector strategies that need barriers, such as generational or concurrent collectors. However, we shouldn't write off this possibility entirely; an ideal replacement for Guile's GC will offer the possibility of migration to other GC designs without imposing new requirements on C API users in the initial phase.

In this regard, the Whippet experiment also has the goal of identifying a smallish GC abstraction in Guile, so that we might consider evolving GC implementation in the future without too much pain. If we switch away from BDW-GC, we should be able to evaluate that it's a win for a large majority of use cases.

To do

Missing features before Guile can use Whippet

  • Pinning
  • Conservative stacks
  • Conservative data segments
  • Heap growth/shrinking
  • Debugging/tracing
  • Finalizers
  • Weak references / weak maps

Features that would improve Whippet performance

  • Immix-style opportunistic evacuation
  • [ ] Overflow allocation (should just evacuate instead)
  • Generational GC via sticky mark bits
  • Generational GC with semi-space nursery
  • Concurrent marking with SATB barrier

About the name

It sounds better than WIP (work-in-progress) garbage collector, doesn't it? Also apparently a whippet is a kind of dog that is fast for its size. It would be nice if whippet-gc turns out to have this property.

License

gcbench.c, MT_GCBench.c, and MT_GCBench2.c are from https://hboehm.info/gc/gc_bench/ and have a somewhat unclear license. I have modified GCBench significantly so that I can slot in different GC implementations. The GC implementations themselves are available under a MIT-style license, the text of which follows:

Copyright (c) 2022 Andy Wingo

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.