Goroutines – why do I see side-by-side execution only towards the end

Issue

The setup

I wrote this little program to see how threads of execution work. Here’s a simplified version, you can find the full one on github

func f(from string) { // Bench
    // Loop for i < 40 -> fib(i)
}

func fib(n int64) int { // Something non-linear
    // Fibonacci implementation
}

func main() {

    go f("|||") // <- Should alternate
    go f("---") // <-

    var input string
    fmt.Scanln(&input)
    fmt.Println("done")
}

As for output, the very first two lines

||| fib( 0 ): 0
--- fib( 0 ): 0

Then it’s all --- up to

--- fib( 28 ): 317811

At which point ||| takes over and goes solo up to

||| fib( 29 ): 514229

And towards the end, they start executing "side-by-side", the complete test dump is here

--- fib( 36 ): 14930352
||| fib( 36 ): 14930352
--- fib( 37 ): 24157817
||| fib( 37 ): 24157817
--- fib( 38 ): 39088169
||| fib( 38 ): 39088169
--- fib( 39 ): 63245986
||| fib( 39 ): 63245986

The question

My initial assumption was – with go f() I should get a relatively random side-by-side execution, but instead it alternates in chunks of varying size that converge way after 29 calls to ‘f()’ to first-second. Why is that so? What could be a better test?

Solution

There are a number of things at play here.

The Go runtime is free to schedule your goroutines to run in whatever order and for whatever duration it pleases. So the assumption that you should get “side-by-side” execution isn’t quite valid. Also, most likely you have not set GOMAXPROCS, which means the runtime by default uses a single core of your cpu. If you set GOMAXPROCS > 1, the runtime has the option of scheduling your goroutines on multiple kernel threads, which in turn could be scheduled by the kernel to run on multiple cores of your CPU. This may give you kind of output your are expecting.

Answered By – Seth Hoenig

Answer Checked By – Timothy Miller (GoLangFix Admin)

Leave a Reply

Your email address will not be published.