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-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2 cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.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 | |