@@ -133,7 +133,7 @@ fn vgc_scan_range(lo usize, hi usize) {
133133 mut addr := start
134134 for addr + sizeof (usize) < = hi {
135135 val := unsafe { * (& usize (voidptr (addr))) }
136- if vgc_is_heap_ptr ( val) {
136+ if val != 0 {
137137 vgc_shade (val)
138138 }
139139 addr + = sizeof (usize)
@@ -143,6 +143,9 @@ fn vgc_scan_range(lo usize, hi usize) {
143143// Shade marks an object grey (discovered but not yet scanned).
144144// Translated from Go's shade() in mgcmark.go.
145145fn vgc_shade (addr usize) {
146+ if addr < vgc_arena_lo || addr > = vgc_arena_hi {
147+ return
148+ }
146149 span := vgc_find_span (voidptr (addr))
147150 if span == unsafe { nil } || ! span.in_use {
148151 return
@@ -174,21 +177,26 @@ fn vgc_shade(addr usize) {
174177// Parallel mark using OS threads.
175178// Translated from Go's gcDrain() with multiple workers.
176179fn vgc_parallel_mark () {
177- // Use up to 4 workers (like Go's dedicated mark workers)
178- nworkers := if vgc_heap.ncaches < 4 { 1 } else { 4 }
180+ mut nworkers := C.vgc_num_cpus ()
181+ if nworkers < 1 {
182+ nworkers = 1
183+ } else if nworkers > 4 {
184+ nworkers = 4
185+ }
179186 vgc_heap.gc_nworkers = nworkers
180187 C.vgc_atomic_store_u32 (& vgc_heap.gc_workers_done, 0 )
181188
182189 if nworkers < = 1 {
183- // Single-threaded mark
184190 vgc_drain_mark_work ()
185191 return
186192 }
187193
188- // Start mark workers as OS threads
189- for _ in 0 .. nworkers {
194+ // Start helper workers and let the current GC thread participate as well.
195+ for _ in 1 .. nworkers {
190196 C.vgc_start_thread (vgc_mark_worker)
191197 }
198+ vgc_drain_mark_work ()
199+ C.vgc_atomic_add_u32 (& vgc_heap.gc_workers_done, 1 )
192200
193201 // Wait for all workers to finish
194202 for C.vgc_atomic_load_u32 (& vgc_heap.gc_workers_done) < u32 (nworkers) {
@@ -264,7 +272,7 @@ fn vgc_scan_precise(obj_addr usize, ptrmap u64, ptr_words u8) {
264272 // Read the pointer at this offset
265273 ptr_addr := obj_addr + usize (bit) * word_size
266274 val := unsafe { * (& usize (voidptr (ptr_addr))) }
267- if val != 0 && vgc_is_heap_ptr (val) {
275+ if val != 0 {
268276 vgc_shade (val)
269277 }
270278 // Clear this bit and continue
@@ -276,8 +284,41 @@ fn vgc_scan_precise(obj_addr usize, ptrmap u64, ptr_words u8) {
276284// Work queue (translated from Go's mgcwork.go)
277285// ============================================================
278286
287+ @[inline]
288+ fn vgc_can_use_work_fastpath () bool {
289+ return vgc_heap.ncaches < = 1 && vgc_heap.gc_nworkers < = 1
290+ }
291+
279292// Add a pointer to the mark work queue
280293fn vgc_work_put (addr usize) {
294+ if vgc_can_use_work_fastpath () {
295+ mut buf := vgc_heap.work_full
296+ if buf == unsafe { nil } || buf.nobj > = 256 {
297+ mut new_buf := vgc_heap.work_empty
298+ if new_buf != unsafe { nil } {
299+ unsafe {
300+ vgc_heap.work_empty = new_buf.next
301+ }
302+ } else {
303+ new_buf = unsafe { & VGC_WorkBuf (C.vgc_os_alloc (usize (sizeof (VGC_WorkBuf)))) }
304+ if new_buf == unsafe { nil } {
305+ return
306+ }
307+ }
308+ unsafe {
309+ new_buf.nobj = 0
310+ new_buf.next = vgc_heap.work_full
311+ vgc_heap.work_full = new_buf
312+ }
313+ buf = new_buf
314+ }
315+ unsafe {
316+ buf.obj[buf.nobj] = addr
317+ buf.nobj++
318+ }
319+ return
320+ }
321+
281322 C.vgc_mutex_lock (& vgc_heap.work_lock)
282323
283324 // Get or create a work buffer
@@ -313,6 +354,23 @@ fn vgc_work_put(addr usize) {
313354
314355// Get a pointer from the mark work queue
315356fn vgc_work_get () usize {
357+ if vgc_can_use_work_fastpath () {
358+ mut buf := vgc_heap.work_full
359+ if buf == unsafe { nil } || buf.nobj == 0 {
360+ return 0
361+ }
362+ unsafe {
363+ buf.nobj--
364+ addr := buf.obj[buf.nobj]
365+ if buf.nobj == 0 {
366+ vgc_heap.work_full = buf.next
367+ buf.next = vgc_heap.work_empty
368+ vgc_heap.work_empty = buf
369+ }
370+ return addr
371+ }
372+ }
373+
316374 C.vgc_mutex_lock (& vgc_heap.work_lock)
317375
318376 mut buf := vgc_heap.work_full
@@ -350,12 +408,8 @@ fn vgc_write_barrier(new_val voidptr) {
350408 if new_val == unsafe { nil } {
351409 return
352410 }
353- addr := usize (new_val)
354- if ! vgc_is_heap_ptr (addr) {
355- return
356- }
357411 // Shade the new pointer (mark it grey)
358- vgc_shade (addr )
412+ vgc_shade (usize (new_val) )
359413}
360414
361415// ============================================================
@@ -471,9 +525,9 @@ fn vgc_update_trigger() {
471525 gc_percent := u64 (vgc_heap.gc_percent)
472526
473527 mut goal := marked + marked * gc_percent / 100
474- // Minimum 4MB trigger
475- if goal < 4 * 1024 * 1024 {
476- goal = 4 * 1024 * 1024
528+ // Avoid very small heap goals that force frequent full cycles on bursty workloads.
529+ if goal < 256 * 1024 * 1024 {
530+ goal = 256 * 1024 * 1024
477531 }
478532 C.vgc_atomic_store_u64 (& vgc_heap.next_gc, goal)
479533}
0 commit comments