v2 / vlib / goroutines / examples / goroutine_benchmark.v
157 lines · 126 sloc · 3.88 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1// Goroutine benchmark: measures scheduling and channel throughput
2// of V's goroutine runtime (translated from Go's GMP scheduler).
3//
4// Compile: v -enable-globals -cc gcc -gc none -prod run goroutine_benchmark.v
5// For comparison, see goroutine_benchmark.go.
6//
7// Note: Under the v1 compiler, the goroutines module uses pthreads-based
8// sync.Mutex which has limitations with ucontext-based context switching.
9// The v2 compiler will use a native spinlock, enabling higher scale.
10module main
11
12import goroutines
13import sync
14import time
15
16__global bench_sem = sync.Semaphore{}
17__global bench_n = int(0)
18__global bench_chan1 = &goroutines.Chan(unsafe { nil })
19__global bench_chan2 = &goroutines.Chan(unsafe { nil })
20__global bench_msgs_per = int(0)
21
22// --- fan-out/fan-in ---
23
24fn fan_worker(arg voidptr) {
25 c := unsafe { &goroutines.Chan(arg) }
26 mut val := 1
27 goroutines.chan_send(c, voidptr(&val), true)
28}
29
30fn fan_collector(arg voidptr) {
31 c := unsafe { &goroutines.Chan(arg) }
32 mut recv_val := 0
33 for _ in 0 .. bench_n {
34 goroutines.chan_recv(c, voidptr(&recv_val), true)
35 }
36 bench_sem.post()
37}
38
39fn bench_fan_out_fan_in(n int) {
40 bench_n = n
41 c := goroutines.chan_make(int(sizeof(int)), 0)
42
43 sw := time.new_stopwatch()
44
45 goroutines.goroutine_create(voidptr(&fan_collector), voidptr(c), 0)
46 for _ in 0 .. n {
47 goroutines.goroutine_create(voidptr(&fan_worker), voidptr(c), 0)
48 }
49
50 bench_sem.wait()
51
52 elapsed := sw.elapsed()
53 us := elapsed.microseconds()
54 ns_per := elapsed.nanoseconds() / n
55 C.printf(c'fan-out/fan-in %d goroutines: %lld us (%lld ns/goroutine)\n', n, us, ns_per)
56}
57
58// --- ping-pong ---
59
60fn ping_side(arg voidptr) {
61 mut val := 0
62 for _ in 0 .. bench_n {
63 goroutines.chan_send(bench_chan1, voidptr(&val), true)
64 goroutines.chan_recv(bench_chan2, voidptr(&val), true)
65 }
66 bench_sem.post()
67}
68
69fn pong_side(arg voidptr) {
70 mut val := 0
71 for _ in 0 .. bench_n {
72 goroutines.chan_recv(bench_chan1, voidptr(&val), true)
73 val++
74 goroutines.chan_send(bench_chan2, voidptr(&val), true)
75 }
76}
77
78fn bench_ping_pong(n int) {
79 bench_n = n
80 bench_chan1 = goroutines.chan_make(int(sizeof(int)), 1)
81 bench_chan2 = goroutines.chan_make(int(sizeof(int)), 1)
82
83 sw := time.new_stopwatch()
84
85 goroutines.goroutine_create(voidptr(&pong_side), unsafe { nil }, 0)
86 goroutines.goroutine_create(voidptr(&ping_side), unsafe { nil }, 0)
87
88 bench_sem.wait()
89
90 elapsed := sw.elapsed()
91 us := elapsed.microseconds()
92 ns_per := elapsed.nanoseconds() / n
93 C.printf(c'ping-pong %d round-trips: %lld us (%lld ns/round-trip)\n', n, us, ns_per)
94}
95
96// --- contended channel ---
97
98fn producer(arg voidptr) {
99 c := unsafe { &goroutines.Chan(arg) }
100 mut val := 0
101 for _ in 0 .. bench_msgs_per {
102 val++
103 goroutines.chan_send(c, voidptr(&val), true)
104 }
105}
106
107fn consumer_fn(arg voidptr) {
108 c := unsafe { &goroutines.Chan(arg) }
109 mut recv_val := 0
110 for _ in 0 .. bench_n {
111 goroutines.chan_recv(c, voidptr(&recv_val), true)
112 }
113 bench_sem.post()
114}
115
116fn bench_contended_channel(num_producers int, msgs_per_producer int) {
117 total := num_producers * msgs_per_producer
118 bench_n = total
119 bench_msgs_per = msgs_per_producer
120 c := goroutines.chan_make(int(sizeof(int)), 64)
121
122 sw := time.new_stopwatch()
123
124 goroutines.goroutine_create(voidptr(&consumer_fn), voidptr(c), 0)
125 for _ in 0 .. num_producers {
126 goroutines.goroutine_create(voidptr(&producer), voidptr(c), 0)
127 }
128
129 bench_sem.wait()
130
131 elapsed := sw.elapsed()
132 us := elapsed.microseconds()
133 ns_per := elapsed.nanoseconds() / total
134 C.printf(c'contended chan %d producers x %d msgs: %lld us (%lld ns/msg)\n', num_producers,
135 msgs_per_producer, us, ns_per)
136}
137
138fn main() {
139 C.printf(c'=== V Goroutine Benchmark ===\n\n')
140
141 bench_fan_out_fan_in(10)
142 bench_fan_out_fan_in(50)
143 bench_fan_out_fan_in(100)
144 bench_fan_out_fan_in(500)
145
146 C.printf(c'\n')
147 bench_ping_pong(1000)
148 bench_ping_pong(10000)
149 bench_ping_pong(100000)
150
151 C.printf(c'\n')
152 bench_contended_channel(4, 1000)
153 bench_contended_channel(10, 1000)
154
155 C.printf(c'\n')
156 goroutines.shutdown()
157}
158