diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt index 7f99bf36b1..eae53c59f2 100644 --- a/cmd/v/help/build-c.txt +++ b/cmd/v/help/build-c.txt @@ -71,10 +71,16 @@ see also `v help build`. more performant without autofree. -gc - Use and link an optional garbage collector. - Only `-gc boehm` and `-gc boehm_leak` are supported currently. - You need to install a `libgc-dev` package first, or install it manually - from source: + Use and link an optional garbage collector. Only tThe Boehm–Demers–Weiser + garbage collector is supported currently with the following sub-options: + + `-gc boehm` ........ selects the default mode for the architecture + `-gc boehm_full` ... full garbage collection mode + `-gc boehm_incr` ... incremental/generational garbage collection mode + `-gc boehm_leak` ... leak detection mode + + You need to install a `libgc-dev` package first, or install it manually from: + https://github.com/ivmai/bdwgc Note, `-gc boehm` is complementary to -autofree. The Boehm garbage diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 2c5fe7e02c..166d3e9529 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -423,7 +423,7 @@ pub fn (mut g Gen) init() { } g.comptime_defines.writeln('') } - if g.pref.gc_mode in [.boehm, .boehm_leak] { + if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm, .boehm_leak] { g.comptime_defines.writeln('#define _VGCBOEHM (1)') } if g.pref.is_debug || 'debug' in g.pref.compile_defines { diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index d08dc766f1..50dea5e198 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -67,12 +67,15 @@ fn (mut g Gen) gen_c_main_function_header() { fn (mut g Gen) gen_c_main_header() { g.gen_c_main_function_header() - if g.pref.gc_mode in [.boehm, .boehm_leak] { + if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm, .boehm_leak] { g.writeln('#if defined(_VGCBOEHM)') if g.pref.gc_mode == .boehm_leak { g.writeln('\tGC_set_find_leak(1);') } g.writeln('\tGC_INIT();') + if g.pref.gc_mode == .boehm_incr { + g.writeln('\tGC_enable_incremental();') + } g.writeln('#endif') } g.writeln('\t_vinit(___argc, (voidptr)___argv);') @@ -158,12 +161,15 @@ pub fn (mut g Gen) gen_c_main_for_tests() { main_fn_start_pos := g.out.len g.writeln('') g.gen_c_main_function_header() - if g.pref.gc_mode in [.boehm, .boehm_leak] { + if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm, .boehm_leak] { g.writeln('#if defined(_VGCBOEHM)') if g.pref.gc_mode == .boehm_leak { g.writeln('\tGC_set_find_leak(1);') } g.writeln('\tGC_INIT();') + if g.pref.gc_mode == .boehm_incr { + g.writeln('\tGC_enable_incremental();') + } g.writeln('#endif') } g.writeln('\t_vinit(___argc, (voidptr)___argv);') diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index e30c9c06ca..5809914557 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -19,8 +19,10 @@ pub enum BuildMode { pub enum GarbageCollectionMode { no_gc - boehm - boehm_leak + boehm_full // full garbage collection mode + boehm_incr // incremental garbage colletion mode + boehm // default Boehm-GC mode for architecture + boehm_leak // leak detection mode (makes `gc_check_leaks()` work) } pub enum OutputMode { @@ -159,7 +161,7 @@ pub mut: build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache cache_manager vcache.CacheManager is_help bool // -h, -help or --help was passed - gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak + gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak, ... // checker settings: checker_match_exhaustive_cutoff_limit int = 10 } @@ -227,6 +229,16 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences '' { res.gc_mode = .no_gc } + 'boehm_full' { + res.gc_mode = .boehm_full + parse_define(mut res, 'gcboehm') + parse_define(mut res, 'gcboehm_full') + } + 'boehm_incr' { + res.gc_mode = .boehm_incr + parse_define(mut res, 'gcboehm') + parse_define(mut res, 'gcboehm_incr') + } 'boehm' { res.gc_mode = .boehm parse_define(mut res, 'gcboehm') @@ -237,7 +249,7 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences parse_define(mut res, 'gcboehm_leak') } else { - eprintln('unknown garbage collection mode, only `-gc boehm` and `-gc boehm_leak` are supported') + eprintln('unknown garbage collection mode, only `-gc boehm`, `-gc boehm_incr`, `-gc boehm_full` and `-gc boehm_leak` are supported') exit(1) } } diff --git a/vlib/v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.pdf b/vlib/v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.pdf new file mode 100644 index 0000000000..e5d55d6121 Binary files /dev/null and b/vlib/v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.pdf differ diff --git a/vlib/v/tests/bench/gcboehm/GC_bench.plt b/vlib/v/tests/bench/gcboehm/GC_bench.plt new file mode 100644 index 0000000000..0815a6309b --- /dev/null +++ b/vlib/v/tests/bench/gcboehm/GC_bench.plt @@ -0,0 +1,9 @@ +#!/usr/bin/gnuplot -persist +set title "Boehm-GC: Full vs. Incremental/Generational Mode" +set xlabel "Interval #" +set ylabel "Pause Time [ms]" +set terminal pdfcairo transparent enhanced fontscale 0.5 size 5.00in, 3.00in +set output "GC_bench.pdf" +plot "boehm_full.txt" title "full GC" w i, "boehm_incr.txt" title "incr/generational GC" w i +set output +# EOF diff --git a/vlib/v/tests/bench/gcboehm/GC_bench.v b/vlib/v/tests/bench/gcboehm/GC_bench.v new file mode 100644 index 0000000000..ac32b6211e --- /dev/null +++ b/vlib/v/tests/bench/gcboehm/GC_bench.v @@ -0,0 +1,46 @@ +import time +import rand +import math + +struct MemObj { +mut: + nxt []&MemObj +} + +const ( + log2n = 10 + n = 1 << log2n + n4 = f64(u64(1) << (4 * log2n)) +) + +fn waste_mem() { + mut objs := MemObj{ + nxt: []&MemObj{len: n} + } + for { + sz := rand.int_in_range(10, 100000) + mut new_obj := &MemObj{ + nxt: []&MemObj{len: sz} + } + sz2 := rand.int_in_range(10, 100000) + new_obj2 := &MemObj{ + nxt: []&MemObj{len: sz2} + } + idx2 := rand.int_in_range(0, sz / 2) + new_obj.nxt[idx2] = new_obj2 + // non-equally distributed random index + idx := int(math.sqrt(math.sqrt(rand.f64n(n4)))) + objs.nxt[idx] = new_obj + } +} + +fn main() { + go waste_mem() + mut last := time.sys_mono_now() + for _ in 0 .. 10_000_000 { + now := time.sys_mono_now() + interval := now - last + println(f64(interval) / f64(time.millisecond)) + last = now + } +} diff --git a/vlib/v/tests/bench/gcboehm/Makefile b/vlib/v/tests/bench/gcboehm/Makefile new file mode 100644 index 0000000000..2437a73492 --- /dev/null +++ b/vlib/v/tests/bench/gcboehm/Makefile @@ -0,0 +1,26 @@ +.PHONY: all + +all: GC_bench.pdf + +GC_bench.pdf: boehm_full.txt boehm_incr.txt + gnuplot GC_bench.plt + @echo "$@ created" + +boehm_full.txt: GC_bench_full + sync + sleep 1 + ./$< > $@ + +boehm_incr.txt: GC_bench_incr + sync + sleep 1 + ./$< > $@ + +GC_bench_full: GC_bench.v + v -prod -gc boehm_full -o $@ $< + +GC_bench_incr: GC_bench.v + v -prod -gc boehm_incr -o $@ $< + +clean: + rm -f boehm_full.txt boehm_incr.txt GC_bench.pdf GC_bench_full GC_bench_incr