Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,26 @@
|
||||
// Example: Basic goroutine usage with `go` keyword.
|
||||
//
|
||||
// This demonstrates how V's `go` keyword launches goroutines
|
||||
// using the GMP scheduler (translated from Go's runtime).
|
||||
//
|
||||
// `go` launches a lightweight goroutine (like Go's goroutines)
|
||||
// `spawn` launches an OS thread (like V's traditional threads)
|
||||
module main
|
||||
|
||||
import time
|
||||
|
||||
fn say(msg string) {
|
||||
for i in 0 .. 5 {
|
||||
println('${msg}: ${i}')
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Launch goroutines with `go` - lightweight, Go-style concurrency
|
||||
go say('goroutine 1')
|
||||
go say('goroutine 2')
|
||||
|
||||
// Main goroutine continues running
|
||||
say('main')
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Goroutine benchmark in Go – equivalent to goroutine_benchmark.v.
|
||||
//
|
||||
// Tests:
|
||||
// 1. Goroutine creation + completion (fan-out/fan-in via unbuffered channel)
|
||||
// 2. Channel ping-pong between two goroutines
|
||||
// 3. Many goroutines contending on a single channel
|
||||
//
|
||||
// Run: go run goroutine_benchmark.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Benchmark 1 – fan-out / fan-in (unbuffered channel)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func benchFanOutFanIn(n int) {
|
||||
c := make(chan int) // unbuffered, matching V benchmark
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
c <- 1
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
<-c
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
fmt.Printf("fan-out/fan-in %d goroutines: %d us (%d ns/goroutine)\n",
|
||||
n, elapsed.Microseconds(), elapsed.Nanoseconds()/int64(n))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Benchmark 2 – channel ping-pong
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func benchPingPong(n int) {
|
||||
c1 := make(chan int, 1)
|
||||
c2 := make(chan int, 1)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < n; i++ {
|
||||
val := <-c1
|
||||
c2 <- val + 1
|
||||
}
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
val := 0
|
||||
for i := 0; i < n; i++ {
|
||||
c1 <- val
|
||||
val = <-c2
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
fmt.Printf("ping-pong %d round-trips: %d us (%d ns/round-trip)\n",
|
||||
n, elapsed.Microseconds(), elapsed.Nanoseconds()/int64(n))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Benchmark 3 – contended channel (many producers, one consumer)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func benchContendedChannel(numProducers int, msgsPerProducer int) {
|
||||
total := numProducers * msgsPerProducer
|
||||
c := make(chan int, 64)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for p := 0; p < numProducers; p++ {
|
||||
go func() {
|
||||
for i := 0; i < msgsPerProducer; i++ {
|
||||
c <- i
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < total; i++ {
|
||||
<-c
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
fmt.Printf("contended chan %d producers x %d msgs: %d us (%d ns/msg)\n",
|
||||
numProducers, msgsPerProducer,
|
||||
elapsed.Microseconds(), elapsed.Nanoseconds()/int64(total))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== Go Goroutine Benchmark ===")
|
||||
fmt.Println()
|
||||
|
||||
benchFanOutFanIn(10)
|
||||
benchFanOutFanIn(50)
|
||||
benchFanOutFanIn(100)
|
||||
benchFanOutFanIn(500)
|
||||
|
||||
fmt.Println()
|
||||
benchPingPong(1000)
|
||||
benchPingPong(10000)
|
||||
benchPingPong(100000)
|
||||
|
||||
fmt.Println()
|
||||
benchContendedChannel(4, 1000)
|
||||
benchContendedChannel(10, 1000)
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Goroutine benchmark: measures scheduling and channel throughput
|
||||
// of V's goroutine runtime (translated from Go's GMP scheduler).
|
||||
//
|
||||
// Compile: v -enable-globals -cc gcc -gc none -prod run goroutine_benchmark.v
|
||||
// For comparison, see goroutine_benchmark.go.
|
||||
//
|
||||
// Note: Under the v1 compiler, the goroutines module uses pthreads-based
|
||||
// sync.Mutex which has limitations with ucontext-based context switching.
|
||||
// The v2 compiler will use a native spinlock, enabling higher scale.
|
||||
module main
|
||||
|
||||
import goroutines
|
||||
import sync
|
||||
import time
|
||||
|
||||
__global bench_sem = sync.Semaphore{}
|
||||
__global bench_n = int(0)
|
||||
__global bench_chan1 = &goroutines.Chan(unsafe { nil })
|
||||
__global bench_chan2 = &goroutines.Chan(unsafe { nil })
|
||||
__global bench_msgs_per = int(0)
|
||||
|
||||
// --- fan-out/fan-in ---
|
||||
|
||||
fn fan_worker(arg voidptr) {
|
||||
c := unsafe { &goroutines.Chan(arg) }
|
||||
mut val := 1
|
||||
goroutines.chan_send(c, voidptr(&val), true)
|
||||
}
|
||||
|
||||
fn fan_collector(arg voidptr) {
|
||||
c := unsafe { &goroutines.Chan(arg) }
|
||||
mut recv_val := 0
|
||||
for _ in 0 .. bench_n {
|
||||
goroutines.chan_recv(c, voidptr(&recv_val), true)
|
||||
}
|
||||
bench_sem.post()
|
||||
}
|
||||
|
||||
fn bench_fan_out_fan_in(n int) {
|
||||
bench_n = n
|
||||
c := goroutines.chan_make(int(sizeof(int)), 0)
|
||||
|
||||
sw := time.new_stopwatch()
|
||||
|
||||
goroutines.goroutine_create(voidptr(&fan_collector), voidptr(c), 0)
|
||||
for _ in 0 .. n {
|
||||
goroutines.goroutine_create(voidptr(&fan_worker), voidptr(c), 0)
|
||||
}
|
||||
|
||||
bench_sem.wait()
|
||||
|
||||
elapsed := sw.elapsed()
|
||||
us := elapsed.microseconds()
|
||||
ns_per := elapsed.nanoseconds() / n
|
||||
C.printf(c'fan-out/fan-in %d goroutines: %lld us (%lld ns/goroutine)\n', n, us, ns_per)
|
||||
}
|
||||
|
||||
// --- ping-pong ---
|
||||
|
||||
fn ping_side(arg voidptr) {
|
||||
mut val := 0
|
||||
for _ in 0 .. bench_n {
|
||||
goroutines.chan_send(bench_chan1, voidptr(&val), true)
|
||||
goroutines.chan_recv(bench_chan2, voidptr(&val), true)
|
||||
}
|
||||
bench_sem.post()
|
||||
}
|
||||
|
||||
fn pong_side(arg voidptr) {
|
||||
mut val := 0
|
||||
for _ in 0 .. bench_n {
|
||||
goroutines.chan_recv(bench_chan1, voidptr(&val), true)
|
||||
val++
|
||||
goroutines.chan_send(bench_chan2, voidptr(&val), true)
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_ping_pong(n int) {
|
||||
bench_n = n
|
||||
bench_chan1 = goroutines.chan_make(int(sizeof(int)), 1)
|
||||
bench_chan2 = goroutines.chan_make(int(sizeof(int)), 1)
|
||||
|
||||
sw := time.new_stopwatch()
|
||||
|
||||
goroutines.goroutine_create(voidptr(&pong_side), unsafe { nil }, 0)
|
||||
goroutines.goroutine_create(voidptr(&ping_side), unsafe { nil }, 0)
|
||||
|
||||
bench_sem.wait()
|
||||
|
||||
elapsed := sw.elapsed()
|
||||
us := elapsed.microseconds()
|
||||
ns_per := elapsed.nanoseconds() / n
|
||||
C.printf(c'ping-pong %d round-trips: %lld us (%lld ns/round-trip)\n', n, us, ns_per)
|
||||
}
|
||||
|
||||
// --- contended channel ---
|
||||
|
||||
fn producer(arg voidptr) {
|
||||
c := unsafe { &goroutines.Chan(arg) }
|
||||
mut val := 0
|
||||
for _ in 0 .. bench_msgs_per {
|
||||
val++
|
||||
goroutines.chan_send(c, voidptr(&val), true)
|
||||
}
|
||||
}
|
||||
|
||||
fn consumer_fn(arg voidptr) {
|
||||
c := unsafe { &goroutines.Chan(arg) }
|
||||
mut recv_val := 0
|
||||
for _ in 0 .. bench_n {
|
||||
goroutines.chan_recv(c, voidptr(&recv_val), true)
|
||||
}
|
||||
bench_sem.post()
|
||||
}
|
||||
|
||||
fn bench_contended_channel(num_producers int, msgs_per_producer int) {
|
||||
total := num_producers * msgs_per_producer
|
||||
bench_n = total
|
||||
bench_msgs_per = msgs_per_producer
|
||||
c := goroutines.chan_make(int(sizeof(int)), 64)
|
||||
|
||||
sw := time.new_stopwatch()
|
||||
|
||||
goroutines.goroutine_create(voidptr(&consumer_fn), voidptr(c), 0)
|
||||
for _ in 0 .. num_producers {
|
||||
goroutines.goroutine_create(voidptr(&producer), voidptr(c), 0)
|
||||
}
|
||||
|
||||
bench_sem.wait()
|
||||
|
||||
elapsed := sw.elapsed()
|
||||
us := elapsed.microseconds()
|
||||
ns_per := elapsed.nanoseconds() / total
|
||||
C.printf(c'contended chan %d producers x %d msgs: %lld us (%lld ns/msg)\n', num_producers,
|
||||
msgs_per_producer, us, ns_per)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
C.printf(c'=== V Goroutine Benchmark ===\n\n')
|
||||
|
||||
bench_fan_out_fan_in(10)
|
||||
bench_fan_out_fan_in(50)
|
||||
bench_fan_out_fan_in(100)
|
||||
bench_fan_out_fan_in(500)
|
||||
|
||||
C.printf(c'\n')
|
||||
bench_ping_pong(1000)
|
||||
bench_ping_pong(10000)
|
||||
bench_ping_pong(100000)
|
||||
|
||||
C.printf(c'\n')
|
||||
bench_contended_channel(4, 1000)
|
||||
bench_contended_channel(10, 1000)
|
||||
|
||||
C.printf(c'\n')
|
||||
goroutines.shutdown()
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// V spawn (OS threads) benchmark – for comparison with Go goroutines.
|
||||
//
|
||||
// Uses V's built-in `spawn` (OS threads) and `chan` (V1 channels).
|
||||
// This measures V's current threading primitives vs Go's goroutines.
|
||||
//
|
||||
// Run: v -prod run spawn_benchmark.v
|
||||
module main
|
||||
|
||||
import time
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Benchmark 1 – fan-out / fan-in
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn fan_worker(c chan int) {
|
||||
c <- 1
|
||||
}
|
||||
|
||||
fn bench_fan_out_fan_in(n int) {
|
||||
c := chan int{cap: n}
|
||||
|
||||
sw := time.new_stopwatch()
|
||||
|
||||
for _ in 0 .. n {
|
||||
spawn fan_worker(c)
|
||||
}
|
||||
|
||||
for _ in 0 .. n {
|
||||
_ = <-c
|
||||
}
|
||||
|
||||
elapsed := sw.elapsed()
|
||||
us := elapsed.microseconds()
|
||||
ns_per := elapsed.nanoseconds() / n
|
||||
println('fan-out/fan-in ${n} threads: ${us} us (${ns_per} ns/thread)')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Benchmark 2 – channel ping-pong
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn pinger(c1 chan int, c2 chan int, n int) {
|
||||
mut val := 0
|
||||
for _ in 0 .. n {
|
||||
val = <-c1
|
||||
val++
|
||||
c2 <- val
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_ping_pong(n int) {
|
||||
c1 := chan int{cap: 1}
|
||||
c2 := chan int{cap: 1}
|
||||
|
||||
spawn pinger(c1, c2, n)
|
||||
|
||||
sw := time.new_stopwatch()
|
||||
|
||||
mut val := 0
|
||||
for _ in 0 .. n {
|
||||
c1 <- val
|
||||
val = <-c2
|
||||
}
|
||||
|
||||
elapsed := sw.elapsed()
|
||||
us := elapsed.microseconds()
|
||||
ns_per := elapsed.nanoseconds() / n
|
||||
println('ping-pong ${n} round-trips: ${us} us (${ns_per} ns/round-trip)')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Benchmark 3 – contended channel (many producers, one consumer)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn producer(c chan int, count int) {
|
||||
mut val := 0
|
||||
for _ in 0 .. count {
|
||||
val++
|
||||
c <- val
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_contended_channel(num_producers int, msgs_per_producer int) {
|
||||
total := num_producers * msgs_per_producer
|
||||
c := chan int{cap: 64}
|
||||
|
||||
sw := time.new_stopwatch()
|
||||
|
||||
for _ in 0 .. num_producers {
|
||||
spawn producer(c, msgs_per_producer)
|
||||
}
|
||||
|
||||
for _ in 0 .. total {
|
||||
_ = <-c
|
||||
}
|
||||
|
||||
elapsed := sw.elapsed()
|
||||
us := elapsed.microseconds()
|
||||
ns_per := elapsed.nanoseconds() / total
|
||||
println('contended chan ${num_producers} producers x ${msgs_per_producer} msgs: ${us} us (${ns_per} ns/msg)')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn main() {
|
||||
println('=== V spawn (OS threads) Benchmark ===')
|
||||
println('')
|
||||
|
||||
bench_fan_out_fan_in(1000)
|
||||
bench_fan_out_fan_in(10000)
|
||||
// Skip 100k - too many OS threads
|
||||
|
||||
println('')
|
||||
bench_ping_pong(10000)
|
||||
bench_ping_pong(100000)
|
||||
|
||||
println('')
|
||||
bench_contended_channel(10, 1000)
|
||||
bench_contended_channel(100, 1000)
|
||||
|
||||
println('')
|
||||
}
|
||||
Reference in new issue
Block a user