aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/sync/bench
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/sync/bench
downloadcli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz
cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2
cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/vlib/sync/bench')
-rw-r--r--v_windows/v/vlib/sync/bench/channel_bench_go.go68
-rw-r--r--v_windows/v/vlib/sync/bench/channel_bench_v.v64
-rw-r--r--v_windows/v/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v150
-rw-r--r--v_windows/v/vlib/sync/bench/results.md48
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 |