diff options
| author | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
|---|---|---|
| committer | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
| commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
| tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/sync/bench | |
| download | cli-tools-windows-master.tar.gz cli-tools-windows-master.tar.bz2 cli-tools-windows-master.zip  | |
Diffstat (limited to 'v_windows/v/vlib/sync/bench')
| -rw-r--r-- | v_windows/v/vlib/sync/bench/channel_bench_go.go | 68 | ||||
| -rw-r--r-- | v_windows/v/vlib/sync/bench/channel_bench_v.v | 64 | ||||
| -rw-r--r-- | v_windows/v/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v | 150 | ||||
| -rw-r--r-- | v_windows/v/vlib/sync/bench/results.md | 48 | 
4 files changed, 330 insertions, 0 deletions
diff --git a/v_windows/v/vlib/sync/bench/channel_bench_go.go b/v_windows/v/vlib/sync/bench/channel_bench_go.go new file mode 100644 index 0000000..a0afbbc --- /dev/null +++ b/v_windows/v/vlib/sync/bench/channel_bench_go.go @@ -0,0 +1,68 @@ +package main + +import "fmt" +import "log" +import "os" +import "time" +import "strconv" + +func assert_eq(a, b int64) { +	if a != b { +		log.Fatalf("assertion failed\nleft: %d, right: %d\n", a, b) +	} +} + +func do_rec(ch chan int32, resch chan int64, n int32) { +	var sum int64 +	var i int32 +	for i = 0; i < n; i++ { +		sum += int64(<- ch) +	} +	fmt.Println(sum) +	resch <- sum +} + +func do_send(ch chan int32, start, end int32) { +	for i := start; i < end; i++ { +		ch <- i +	} +} + +func main() { +	if len(os.Args) != 5 { +		log.Fatalf("usage:\n\t%s <nsend> <nrec> <buflen> <nobj>\n", os.Args[0]) +	} +	nsend, _ := strconv.Atoi(os.Args[1]) +	nrec, _ := strconv.Atoi(os.Args[2]) +	buflen, _ := strconv.Atoi(os.Args[3]) +	nobj, _ := strconv.Atoi(os.Args[4]) +	stopwatch := time.Now() +	ch := make(chan int32, buflen) +	resch := make(chan int64, 0) +	no := nobj +	for i := 0; i < nrec; i++ { +		n := no / (nrec - i) +		go do_rec(ch, resch, int32(n)) +		no -= n +	} +	assert_eq(int64(no), 0)	 +	no = nobj +	for i := 0; i < nsend; i++ { +		n := no / (nsend - i) +		end := no +		no -= n +		go do_send(ch, int32(no), int32(end)) +	} +	assert_eq(int64(no), 0)	 +	var sum int64 +	for i := 0; i < nrec; i++ { +		sum += <-resch +	} +	elapsed := time.Now().Sub(stopwatch) +	rate := float64(nobj)/float64(elapsed.Nanoseconds())*1000.0 +	duration := 1.0e-09 * float64(elapsed.Nanoseconds()) +	fmt.Printf("%d objects in %g s (%.2f objs/µs)\n", nobj, duration, rate) +	expected_sum := int64(nobj)*int64(nobj-1)/2 +	fmt.Printf("got: %d, expected: %d\n", sum, expected_sum) +	assert_eq(sum, expected_sum) +} diff --git a/v_windows/v/vlib/sync/bench/channel_bench_v.v b/v_windows/v/vlib/sync/bench/channel_bench_v.v new file mode 100644 index 0000000..54dcfe9 --- /dev/null +++ b/v_windows/v/vlib/sync/bench/channel_bench_v.v @@ -0,0 +1,64 @@ +// Channel Benchmark +// +// `nobj` integers are sent thru a channel with queue length`buflen` +// using `nsend` sender threads and `nrec` receiver threads. +// +// The receive threads add all received numbers and send them to the +// main thread where the total sum is compare to the expected value. +import time +import os + +fn do_rec(ch chan int, resch chan i64, n int) { +	mut sum := i64(0) +	for _ in 0 .. n { +		sum += <-ch +	} +	println(sum) +	resch <- sum +} + +fn do_send(ch chan int, start int, end int) { +	for i in start .. end { +		ch <- i +	} +} + +fn main() { +	if os.args.len != 5 { +		eprintln('usage:\n\t${os.args[0]} <nsend> <nrec> <buflen> <nobj>') +		exit(1) +	} +	nsend := os.args[1].int() +	nrec := os.args[2].int() +	buflen := os.args[3].int() +	nobj := os.args[4].int() +	stopwatch := time.new_stopwatch() +	ch := chan int{cap: buflen} +	resch := chan i64{} +	mut no := nobj +	for i in 0 .. nrec { +		n := no / (nrec - i) +		go do_rec(ch, resch, n) +		no -= n +	} +	assert no == 0 +	no = nobj +	for i in 0 .. nsend { +		n := no / (nsend - i) +		end := no +		no -= n +		go do_send(ch, no, end) +	} +	assert no == 0 +	mut sum := i64(0) +	for _ in 0 .. nrec { +		sum += <-resch +	} +	elapsed := stopwatch.elapsed() +	rate := f64(nobj) / elapsed * time.microsecond +	println('$nobj objects in ${f64(elapsed) / time.second} s (${rate:.2f} objs/µs)') +	// use sum formula by Gauß to calculate the expected result +	expected_sum := i64(nobj) * (nobj - 1) / 2 +	println('got: $sum, expected: $expected_sum') +	assert sum == expected_sum +} diff --git a/v_windows/v/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v b/v_windows/v/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v new file mode 100644 index 0000000..999bb1d --- /dev/null +++ b/v_windows/v/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v @@ -0,0 +1,150 @@ +import os +import os.cmdline +import time +import sync + +// Usage: +// many_writers_and_receivers_on_1_channel [-readers 1] [-writers 4] [-chan_cap 100] [-iterations 25000] > results.csv +// +// You can then open results.csv in Excel/Calc and for example plot the first vs the second column. +enum EventKind { +	push +	pop +} + +struct Event { +	is_set  bool +	id      int +	gtime   u64 // nanoseconds +	i       int +	kind    EventKind +	elapsed i64 // nanoseconds, elapsed after the previous event of the same kind +} + +struct Context { +mut: +	n_iters   int +	n_readers int +	n_writers int +	// +	pops_wg &sync.WaitGroup +	pops    []Event +	// +	pushes_wg &sync.WaitGroup +	pushes    []Event +} + +fn do_rec(ch chan int, id int, mut ctx Context) { +	eprintln('start of  do_rec id: $id') +	mut timer_sw_x := time.new_stopwatch() +	mut tmp := int(0) +	mut i := int(0) +	// NB: a single receiver thread can get slightly more +	// than its fair share of sends, that is why +	// the receiver's Event array is much larger, +	// enough so a single receiver can potentially process all +	// writers pushes, and it is partitioned over all of +	// id, ctx.n_writers and n_iters: +	n_iters := ctx.n_iters +	base := id * n_iters * ctx.n_writers +	for { +		for ch.try_pop(tmp) == .success { +			ctx.pops[base + i] = Event{ +				is_set: true +				id: id +				gtime: time.sys_mono_now() +				i: i +				kind: .pop +				elapsed: timer_sw_x.elapsed().nanoseconds() +			} +			timer_sw_x.restart() +			i++ +			if tmp == 1 { +				ctx.pops_wg.done() +				return +			} +		} +	} +} + +fn do_send(ch chan int, id int, mut ctx Context) { +	eprintln('start of do_send id: $id') +	mut timer_sw_x := time.new_stopwatch() +	n_iters := ctx.n_iters +	base := n_iters * id // sender events can not overlap +	for i := 0; i < n_iters; i++ { +		idx := base + i +		ctx.pushes[idx] = Event{ +			is_set: true +			id: id +			gtime: time.sys_mono_now() +			i: i +			kind: .push +			elapsed: timer_sw_x.elapsed().nanoseconds() +		} +		timer_sw_x.restart() +		tmp := int(0) +		ch <- tmp +	} +	ctx.pushes_wg.done() +} + +fn main() { +	// +	args := os.args[1..] +	if '-h' in args || '--help' in args { +		eprintln('Usage:\n many_writers_and_receivers_on_1_channel [-readers 1] [-writers 4] [-chan_cap 100] [-iterations 25000]') +		exit(0) +	} +	n_iters := cmdline.option(args, '-iterations', '25000').int() +	n_readers := cmdline.option(args, '-readers', '1').int() +	n_writers := cmdline.option(args, '-writers', '4').int() +	chan_cap := cmdline.option(args, '-chan_cap', '100').int() +	eprintln('> n_iters, $n_iters, n_writers, $n_writers, n_readers, $n_readers, chan_cap, $chan_cap') +	// +	ch := chan int{cap: chan_cap} +	max_number_of_pushes := n_writers * (n_iters + 2) +	max_number_of_pops := max_number_of_pushes * n_readers +	eprintln('> max_number_of_pushes, $max_number_of_pushes, max_number_of_pops (per receiver), $max_number_of_pops') +	mut ctx := &Context{ +		n_iters: n_iters +		n_readers: n_readers +		n_writers: n_writers +		pushes_wg: sync.new_waitgroup() +		pops_wg: sync.new_waitgroup() +		pushes: []Event{len: max_number_of_pushes} +		pops: []Event{len: max_number_of_pops} +	} +	ctx.pops_wg.add(n_readers) +	for i := 0; i < n_readers; i++ { +		go do_rec(ch, i, mut ctx) +	} +	ctx.pushes_wg.add(n_writers) +	for i := 0; i < n_writers; i++ { +		go do_send(ch, i, mut ctx) +	} +	ctx.pushes_wg.wait() +	eprintln('>> all pushes done') +	for i := 0; i < n_readers; i++ { +		ch <- 1 +	} +	ctx.pops_wg.wait() +	eprintln('>> all pops done') +	mut all_events := []Event{} +	all_events << ctx.pops +	all_events << ctx.pushes +	all_events.sort(a.elapsed < b.elapsed) +	mut i := 0 +	for e in all_events { +		if !e.is_set { +			continue +		} +		i++ +		if e.kind == .pop { +			println('${i:8} , ${e.elapsed:10}, ns ,  do_rec id:, ${e.id:3} , i=, ${e.i:5} , ${e.gtime:20}') +		} +		if e.kind == .push { +			println('${i:8} , ${e.elapsed:10}, ns , do_send id:, ${e.id:3} , i=, ${e.i:5} , ${e.gtime:20}') +		} +	} +} diff --git a/v_windows/v/vlib/sync/bench/results.md b/v_windows/v/vlib/sync/bench/results.md new file mode 100644 index 0000000..882471c --- /dev/null +++ b/v_windows/v/vlib/sync/bench/results.md @@ -0,0 +1,48 @@ +# Channel Benchmark Results + +This documents lists several benchmark results for different platforms in order to +identify performance regressions and improvements. + +The are measured using the command + +``` +> channel_bench_* <nsend> <nrec> <buflen> <nobj> + +nsend ... number of threads that push objects into the channel +nrec .... number of threads that pop objects from the channel +buflen .. length of channel buffer queue - `0` means unbuffered channel +nobj .... number of objects to pass thru the channel +``` + +## AMD Ryzen 7 3800X, Ubuntu-20.04 x86_64 + +10000000 Objects transfered, results in Objects/µs + +| nsend | nrec | buflen | **V (gcc -O2)** | **V (clang)** | **V (tcc)** | **Go (golang)** | **Go (gccgo -O2)** | +| :---: | :---:| :---:  |      :---:      |    :---:      |    :---:    |     :---:      |      :---:         | +|   1   |   1  |    0   |      1.97       |    1.63       |    2.08     |      4.65      |       0.56         | +|   1   |   1  |   100  |      3.05       |    2.29       |    1.93     |     18.90      |       6.08         | +|   4   |   4  |    0   |      0.87       |    0.90       |    0.99     |      1.84      |       0.84         | +|   4   |   4  |   100  |      3.35       |    3.07       |    2.92     |      7.43      |       3.71         | + +## AMD Ryzen 7 3800X, Windows 10 2004 x64 + +| nsend | nrec | buflen | **V (gcc -O2)**  | **V (msvc /O2)** | **V (tcc)** | **Go (golang)** | +| :---: | :---:| :---:  |      :---:       |      :---:       |    :---:    |     :---:       | +|   1   |   1  |    0   |      2.30        |      3.76        |    2.02     |      4.67       | +|   1   |   1  |   100  |      2.96        |      3.12        |    2.26     |     23.31       | +|   4   |   4  |    0   |      0.90        |      1.05        |    0.83     |      1.38       | +|   4   |   4  |   100  |      2.28        |      2.16        |    2.43     |      4.63       | + +## Raspberry Pi 3B+, Void Linux musl 32 bit + +10000000 Objects transfered, results in Objects/µs + +| nsend | nrec | buflen | **V (gcc -O2)** | **Go (golang)** | +| :---: | :---:| :---:  |      :---:      |     :---:      | +|   1   |   1  |    0   |      0.37       |     0.21       | +|   1   |   1  |   100  |      1.03       |     0.74       | +|   4   |   4  |    0   |      0.04       |     0.38       | +|   4   |   4  |   100  |      2.78       |     2.63       | +|   2   |   2  |    0   |      0.05       |     0.38       | +|   2   |   2  |   100  |      1.26       |     0.75       |  | 
