Skip to content

gc: add VGC - concurrent tri-color mark-and-sweep GC translated from Go - #26739

Merged
medvednikov merged 7 commits into
masterfrom
claude/translate-gc-to-v-SzpYK
Mar 16, 2026
Merged

medvednikov merged 7 commits into
masterfrom
claude/translate-gc-to-v-SzpYK

Conversation

@medvednikov

Copy link
Copy Markdown
Member

Translate Go's runtime garbage collector to V, available via -gc vgc.

The VGC implements Go's core GC algorithms:

  • Size-class based allocation with 67 classes (8B-32KB), translated from
    Go's runtime/sizeclasses.go
  • Per-thread allocation caches (mcache equivalent) for lock-free fast path
  • Central free lists (mcentral equivalent) with span management
  • Arena-based heap with mmap/VirtualAlloc OS memory backend
  • Tri-color concurrent mark-and-sweep (mgc.go, mgcmark.go, mgcsweep.go)
  • Conservative root scanning of thread stacks
  • Parallel marking using spawn (V threads) - translated from Go's gcDrain
  • Concurrent sweeping in background spawn worker
  • Write barrier support (Dijkstra-style insertion barrier)
  • GC pacing with GOGC-equivalent trigger (heap doubling heuristic)
  • Tiny allocator for small noscan objects (<16 bytes)

New files:

  • thirdparty/vgc/vgc_platform.h: Platform abstractions (atomics, mmap,
    spinlocks, size class tables, bitmap ops) with TCC/MSVC/GCC support
  • vlib/builtin/vgc_d_vgc.c.v: Core types, heap, spans, allocation
  • vlib/builtin/vgc_gc_d_vgc.c.v: Mark, sweep, work queues, orchestration
  • vlib/builtin/builtin_d_vgc.c.v: Compile-define anchor file

Modified files:

  • vlib/v2/pref/pref.v: Add GarbageCollectionMode enum with vgc option
  • vlib/v2/gen/cleanc/fn.v: Inject vgc_init() in main for v2 backend
  • vlib/v/pref/pref.v: Add .vgc variant to GarbageCollectionMode
  • vlib/v/gen/c/cmain.v: Inject vgc_init() in main for v1 backend
  • vlib/builtin/allocation.c.v: Add $if vgc ? branches to malloc/free/etc
  • vlib/builtin/builtin_notd_gcboehm.c.v: Delegate to VGC when active

Usage: v -gc vgc myprogram.v

https://claude.ai/code/session_01X5Zy77TKA9GLAwzTS8AhWM

claude added 2 commits March 16, 2026 13:00
Translate Go's runtime garbage collector to V, available via `-gc vgc`.

The VGC implements Go's core GC algorithms:
- Size-class based allocation with 67 classes (8B-32KB), translated from
  Go's runtime/sizeclasses.go
- Per-thread allocation caches (mcache equivalent) for lock-free fast path
- Central free lists (mcentral equivalent) with span management
- Arena-based heap with mmap/VirtualAlloc OS memory backend
- Tri-color concurrent mark-and-sweep (mgc.go, mgcmark.go, mgcsweep.go)
- Conservative root scanning of thread stacks
- Parallel marking using spawn (V threads) - translated from Go's gcDrain
- Concurrent sweeping in background spawn worker
- Write barrier support (Dijkstra-style insertion barrier)
- GC pacing with GOGC-equivalent trigger (heap doubling heuristic)
- Tiny allocator for small noscan objects (<16 bytes)

New files:
- thirdparty/vgc/vgc_platform.h: Platform abstractions (atomics, mmap,
  spinlocks, size class tables, bitmap ops) with TCC/MSVC/GCC support
- vlib/builtin/vgc_d_vgc.c.v: Core types, heap, spans, allocation
- vlib/builtin/vgc_gc_d_vgc.c.v: Mark, sweep, work queues, orchestration
- vlib/builtin/builtin_d_vgc.c.v: Compile-define anchor file

Modified files:
- vlib/v2/pref/pref.v: Add GarbageCollectionMode enum with vgc option
- vlib/v2/gen/cleanc/fn.v: Inject vgc_init() in main for v2 backend
- vlib/v/pref/pref.v: Add .vgc variant to GarbageCollectionMode
- vlib/v/gen/c/cmain.v: Inject vgc_init() in main for v1 backend
- vlib/builtin/allocation.c.v: Add $if vgc ? branches to malloc/free/etc
- vlib/builtin/builtin_notd_gcboehm.c.v: Delegate to VGC when active

Usage: v -gc vgc myprogram.v

https://claude.ai/code/session_01X5Zy77TKA9GLAwzTS8AhWM

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f91da827e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v/gen/c/cmain.v Outdated
}
if g.pref.gc_mode == .vgc {
g.writeln('\t// VGC initialization (concurrent tri-color mark-and-sweep)')
g.writeln('\tvgc_init();')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Emit the correct mangled VGC init symbol

In the v1 C backend main prologue this emits vgc_init();, but non-method functions in module builtin are emitted with the builtin__ C prefix (see c_fn_name behavior in vlib/v/gen/c/fn.v), so the generated symbol is builtin__vgc_init. With -gc vgc on the v1 backend, this produces an undefined vgc_init at link time instead of initializing the collector.

Useful? React with 👍 / 👎.

unsafe {
vgc_heap.caches[idx].registered = true
vgc_heap.caches[idx].stack_hi = usize(C.vgc_get_sp())
vgc_heap.caches[idx].stack_lo = vgc_heap.caches[idx].stack_hi - usize(8) * 1024 * 1024 // estimate 8MB stack

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Scan the active stack segment for root discovery

Thread registration records stack_hi as the current SP and then sets stack_lo to stack_hi - 8MB, while root scanning later walks [stack_lo, stack_hi]. On the usual downward-growing stacks (x86_64/aarch64 targets), live frames are above the current SP, so this range misses actual roots and can let reachable objects be collected.

Useful? React with 👍 / 👎.

Comment on lines +54 to +55
C.vgc_atomic_store_u32(&vgc_heap.gc_stop_flag, 0)
C.vgc_atomic_fence()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Preserve tri-color invariant when mutators resume

The collector resumes mutators before marking is finished, but this revision has no call sites for vgc_write_barrier (only its definition exists), so pointer writes performed during concurrent mark are not shaded. That breaks the tri-color invariant and can cause newly referenced live objects to remain white and be swept.

Useful? React with 👍 / 👎.

Comment on lines +195 to +199
uint32_t size = (uint32_t)(i + 1) * 8;
while (cls < 67 && vgc_class_sizes[cls] < size) {
cls++;
}
vgc_s2c8[i] = (uint8_t)cls;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fix off-by-one in size-to-class lookup tables

The size-class table is initialized with (i + 1)-scaled sizes stored at index i, while lookup uses shifted indices whose first used entry is 1, not 0. This shifts requests into larger classes than necessary (and the same pattern appears in vgc_s2c128), leading to systematic over-allocation and wrong class selection for small objects.

Useful? React with 👍 / 👎.

claude added 5 commits March 16, 2026 14:35
- Fix C.vgc_mutex_lock/unlock: rename param from `lock` (V keyword) to `lk`
- Remove unused `heap_size` variable in vgc_heap_usage()
- Revert v1 gen/c/cmain.v and v/pref/pref.v changes - only v2 is needed

https://claude.ai/code/session_01X5Zy77TKA9GLAwzTS8AhWM
- Replace `spawn` with C.vgc_start_thread() (pthread/CreateThread) to
  fix builtin -> sync.threads -> builtin import cycle
- Add vgc_start_thread() to platform header for direct OS thread creation
- Add v1 compiler support: pref.v (.vgc enum), cmain.v (builtin__vgc_init call)
- Fix @sizeof -> sizeof, add @[markused] for vgc_init
- Reduce heap struct array sizes to prevent stack overflow on init
- Add bench/bench_gc.v benchmark comparing VGC vs Boehm GC

Benchmark results (gcc -prod):
  VGC:   small=98-371ms, tree=283-1677ms, array=96-417ms, heap=1.5GB
  Boehm: small=63-100ms, tree=139-177ms, array=30-50ms,   heap=43MB

VGC is functional but needs optimization for memory usage and GC pause times.

https://claude.ai/code/session_01X5Zy77TKA9GLAwzTS8AhWM
Major performance fixes bringing VGC within 1.2-1.4x of Boehm:

- Add address map (vgc_addr_map) for O(1) pointer-to-arena lookup,
  replacing linear arena scan in vgc_is_heap_ptr/vgc_find_span
- Add global arena bounds (vgc_arena_lo/hi) for fast pointer rejection
  during conservative stack scanning
- Skip noscan objects in mark work queue (don't scan objects that
  can't contain pointers - this is the V compiler advantage)
- Fix heap_live to track actual object bytes, not page bytes
- Add span recycling: empty spans go to per-page-count free pool,
  pages decommitted via MADV_DONTNEED, reused on next alloc
- Byte-level bitmap scan in alloc (skip 0xFF full bytes)
- Byte-level sweep using popcount (alloc & ~mark = garbage)
- Reset heap_live from mark bits after GC (fixes drift)
- Amortize GC trigger check (only on span fill, not every alloc)
- Fix heap_usage to report actual in-use span pages

Benchmark (gcc -prod, steady state iterations 2-5):
  VGC:   small=242ms, tree=352ms, array=84ms, heap=128MB
  Boehm: small=200ms, tree=280ms, array=85ms, heap=43MB
  Ratio: 1.2x        1.3x        1.0x        3x

https://claude.ai/code/session_01X5Zy77TKA9GLAwzTS8AhWM
Generate per-struct pointer bitmaps at compile time (vgc_ptrmap) so the
GC can scan only known pointer offsets instead of every word. Adds
HEAP_vgc macro, vgc_malloc_typed/vgc_memdup_typed allocators, and
precise scan path in vgc_drain_mark_work. Falls back to conservative
scanning for complex types (interfaces, sumtypes, nested pointer structs).

https://claude.ai/code/session_01X5Zy77TKA9GLAwzTS8AhWM
@medvednikov
medvednikov merged commit a011c78 into master Mar 16, 2026
127 of 179 checks passed
@medvednikov
medvednikov deleted the claude/translate-gc-to-v-SzpYK branch March 23, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants