NOTE

3.1 GMP

Goroutine scheduler, GM model, GMP model, P/M/G, goroutine creation flow, preemptive scheduling, and references.

GoCreated Updated 2 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. Goroutine Scheduler

  • The coroutine scheduling model in Golang: given M threads and N goroutines, how should goroutines be assigned to threads for execution?
  • This is essentially similar to an operating-system scheduler: given M processors and N threads, how should processors be assigned to threads for execution?

1.1. GM Model

  • The GM model was used at first.
    • G: goroutine; M: kernel thread.
    • In this model, all Gs are placed in a global queue. M needs to lock when taking Gs from or putting Gs back into the global queue.
    • Problems:
      • Locking is inefficient.
      • Poor locality.

1.2. GMP Model

  • To solve the disadvantages of the GM model, the GMP model was introduced. M binds to P; P has a local queue that stores goroutines; M takes goroutines from it for execution. If P’s local queue is empty, it gets work from the global queue or steals from another P.
  • In one sentence: M needs to obtain a P before it can run a G.

1.2.1. P

  • Processor, an abstract processor.
  • The GOMAXPROCS environment variable or runtime.GOMAXPROCS() sets how many operating-system threads can execute Go code at the same time, i.e. the number of Ps in GMP.

1.2.2. M

  • Machine, a kernel thread.

  • Ms are created by the Go runtime as needed; the operating-system thread limit can be configured through runtime/debug.SetMaxThreads, whose default limit is 10,000.

  • If an M blocks, a new M will be created.

  • If an M is idle, it will be reclaimed or put to sleep.

1.2.3. G

  • Goroutine, a coroutine.
  • Executes user code.
  • runtime.NumGoroutine() gets the current total number of goroutines, i.e. the number of Gs in GMP.
  • The number of Gs can also be obtained through pprof.md.

2. Flow for the Scheduler to Create a Goroutine

2.1. Initialize M0 and G0

  • M0
    • The main thread with number 0 after the program starts.
    • Responsible for initialization and starting the first G.
  • G0
    • Each time a new M is started, a G is created. This first created G is G0.
    • Responsibilities:
      • Schedule other goroutines that execute on M.
      • Create other goroutines.
      • Execute GC.
      • Grow stacks.

2.2. go func Execution Flow

3. Preemptive Scheduling

At first, coroutine scheduling in user space was cooperative: only after one coroutine yielded the CPU would the next coroutine execute. Later, to prevent one goroutine from running for too long, it was changed to preemptive scheduling.

Preemption timing:

  • Goroutines blocked in system calls for a long time or running for a relatively long time may be preempted; Go 1.14 and later also support asynchronous preemption.
  • The runtime has a background detection thread that checks these conditions and notifies goroutines to schedule.

4. References

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub