Search code examples
gomultitaskinggoroutine

Max number of goroutines


How many goroutines can I use painless? For example wikipedia says, in Erlang 20 million processes can be created without degrading performance.

Update: I've just investigated in goroutines performance a little and got such a results:

  • It looks like goroutine lifetime is more then calculating sqrt() 1000 times ( ~45µs for me ), the only limitation is memory
  • Goroutine costs 4 — 4.5 KB

Solution

  • If a goroutine is blocked, there is no cost involved other than:

    • memory usage
    • slower garbage-collection

    The costs (in terms of memory and average time to actually start executing a goroutine) are:

    Go 1.6.2 (April 2016)
      32-bit x86 CPU (A10-7850K 4GHz)
        | Number of goroutines: 100000
        | Per goroutine:
        |   Memory: 4536.84 bytes
        |   Time:   1.634248 µs
      64-bit x86 CPU (A10-7850K 4GHz)
        | Number of goroutines: 100000
        | Per goroutine:
        |   Memory: 4707.92 bytes
        |   Time:   1.842097 µs
    
    Go release.r60.3 (December 2011)
      32-bit x86 CPU (1.6 GHz)
        | Number of goroutines: 100000
        | Per goroutine:
        |   Memory: 4243.45 bytes
        |   Time:   5.815950 µs
    

    On a machine with 4 GB of memory installed, this limits the maximum number of goroutines to slightly less than 1 million.


    Source code (no need to read this if you already understand the numbers printed above):

    package main
    
    import (
        "flag"
        "fmt"
        "os"
        "runtime"
        "time"
    )
    
    var n = flag.Int("n", 1e5, "Number of goroutines to create")
    
    var ch = make(chan byte)
    var counter = 0
    
    func f() {
        counter++
        <-ch // Block this goroutine
    }
    
    func main() {
        flag.Parse()
        if *n <= 0 {
                fmt.Fprintf(os.Stderr, "invalid number of goroutines")
                os.Exit(1)
        }
    
        // Limit the number of spare OS threads to just 1
        runtime.GOMAXPROCS(1)
    
        // Make a copy of MemStats
        var m0 runtime.MemStats
        runtime.ReadMemStats(&m0)
    
        t0 := time.Now().UnixNano()
        for i := 0; i < *n; i++ {
                go f()
        }
        runtime.Gosched()
        t1 := time.Now().UnixNano()
        runtime.GC()
    
        // Make a copy of MemStats
        var m1 runtime.MemStats
        runtime.ReadMemStats(&m1)
    
        if counter != *n {
                fmt.Fprintf(os.Stderr, "failed to begin execution of all goroutines")
                os.Exit(1)
        }
    
        fmt.Printf("Number of goroutines: %d\n", *n)
        fmt.Printf("Per goroutine:\n")
        fmt.Printf("  Memory: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(*n))
        fmt.Printf("  Time:   %f µs\n", float64(t1-t0)/float64(*n)/1e3)
    }