| 1 | // V spawn (OS threads) benchmark – for comparison with Go goroutines. |
| 2 | // |
| 3 | // Uses V's built-in `spawn` (OS threads) and `chan` (V1 channels). |
| 4 | // This measures V's current threading primitives vs Go's goroutines. |
| 5 | // |
| 6 | // Run: v -prod run spawn_benchmark.v |
| 7 | module main |
| 8 | |
| 9 | import time |
| 10 | |
| 11 | // --------------------------------------------------------------------------- |
| 12 | // Benchmark 1 – fan-out / fan-in |
| 13 | // --------------------------------------------------------------------------- |
| 14 | |
| 15 | fn fan_worker(c chan int) { |
| 16 | c <- 1 |
| 17 | } |
| 18 | |
| 19 | fn bench_fan_out_fan_in(n int) { |
| 20 | c := chan int{cap: n} |
| 21 | |
| 22 | sw := time.new_stopwatch() |
| 23 | |
| 24 | for _ in 0 .. n { |
| 25 | spawn fan_worker(c) |
| 26 | } |
| 27 | |
| 28 | for _ in 0 .. n { |
| 29 | _ = <-c |
| 30 | } |
| 31 | |
| 32 | elapsed := sw.elapsed() |
| 33 | us := elapsed.microseconds() |
| 34 | ns_per := elapsed.nanoseconds() / n |
| 35 | println('fan-out/fan-in ${n} threads: ${us} us (${ns_per} ns/thread)') |
| 36 | } |
| 37 | |
| 38 | // --------------------------------------------------------------------------- |
| 39 | // Benchmark 2 – channel ping-pong |
| 40 | // --------------------------------------------------------------------------- |
| 41 | |
| 42 | fn pinger(c1 chan int, c2 chan int, n int) { |
| 43 | mut val := 0 |
| 44 | for _ in 0 .. n { |
| 45 | val = <-c1 |
| 46 | val++ |
| 47 | c2 <- val |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | fn bench_ping_pong(n int) { |
| 52 | c1 := chan int{cap: 1} |
| 53 | c2 := chan int{cap: 1} |
| 54 | |
| 55 | spawn pinger(c1, c2, n) |
| 56 | |
| 57 | sw := time.new_stopwatch() |
| 58 | |
| 59 | mut val := 0 |
| 60 | for _ in 0 .. n { |
| 61 | c1 <- val |
| 62 | val = <-c2 |
| 63 | } |
| 64 | |
| 65 | elapsed := sw.elapsed() |
| 66 | us := elapsed.microseconds() |
| 67 | ns_per := elapsed.nanoseconds() / n |
| 68 | println('ping-pong ${n} round-trips: ${us} us (${ns_per} ns/round-trip)') |
| 69 | } |
| 70 | |
| 71 | // --------------------------------------------------------------------------- |
| 72 | // Benchmark 3 – contended channel (many producers, one consumer) |
| 73 | // --------------------------------------------------------------------------- |
| 74 | |
| 75 | fn producer(c chan int, count int) { |
| 76 | mut val := 0 |
| 77 | for _ in 0 .. count { |
| 78 | val++ |
| 79 | c <- val |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | fn bench_contended_channel(num_producers int, msgs_per_producer int) { |
| 84 | total := num_producers * msgs_per_producer |
| 85 | c := chan int{cap: 64} |
| 86 | |
| 87 | sw := time.new_stopwatch() |
| 88 | |
| 89 | for _ in 0 .. num_producers { |
| 90 | spawn producer(c, msgs_per_producer) |
| 91 | } |
| 92 | |
| 93 | for _ in 0 .. total { |
| 94 | _ = <-c |
| 95 | } |
| 96 | |
| 97 | elapsed := sw.elapsed() |
| 98 | us := elapsed.microseconds() |
| 99 | ns_per := elapsed.nanoseconds() / total |
| 100 | println('contended chan ${num_producers} producers x ${msgs_per_producer} msgs: ${us} us (${ns_per} ns/msg)') |
| 101 | } |
| 102 | |
| 103 | // --------------------------------------------------------------------------- |
| 104 | // Main |
| 105 | // --------------------------------------------------------------------------- |
| 106 | |
| 107 | fn main() { |
| 108 | println('=== V spawn (OS threads) Benchmark ===') |
| 109 | println('') |
| 110 | |
| 111 | bench_fan_out_fan_in(1000) |
| 112 | bench_fan_out_fan_in(10000) |
| 113 | // Skip 100k - too many OS threads |
| 114 | |
| 115 | println('') |
| 116 | bench_ping_pong(10000) |
| 117 | bench_ping_pong(100000) |
| 118 | |
| 119 | println('') |
| 120 | bench_contended_channel(10, 1000) |
| 121 | bench_contended_channel(100, 1000) |
| 122 | |
| 123 | println('') |
| 124 | } |
| 125 | |