GMP concurrency model

Processes and threads and coroutines

Multiple threads belong to the same process and share memory space. Communication between threads is based on shared memory.

The Go language scheduler uses the same number of threads as the CPU to schedule multiple Goroutines.

“Why Go? “

The process or thread is faulty:

  1. High CPU consumption
    • Switching thread context requires applying for and destroying resources and consumes a lot of time
  2. High Memory usage
    • Threads occupy more than 1 MB memory space

Advantages of goroutines:

  1. Less memory footprint (several kilobytes)
    • The initial value is 2kb, which is automatically expanded if the stack space is insufficient
  2. More flexible scheduling (Runtime scheduling)
    • Go’s own implementation of the scheduler, created and destroyed with minimal overhead, is user-level.
  3. Preemptive scheduling (10ms)
    • The compiler inserts a preemption instruction, which checks if the current Goroutine has initiated a preemption request
  4. Support for signal-based asynchronous preemption (20ms) after 1.14
    • Preemption scheduling is triggered when garbage collection scans the stack
    • Solve preemptive scheduling due to garbage collection and recycling for a long time to occupy resources (unable to execute preemption instructions) resulting in program suspension

GMP concurrency model

GMP

G needs to run on M, M depends on the resources provided by P, and P holds G waiting to run. M has no absolute relationship with the number of P. If M blocks, P will create or switch another M.

G: Take the first letter of goroutine, mainly save some state information of Goroutine and some register values of CPU

M: Take the first letter of machine. It stands for a worker thread, or system thread. G needs to be scheduled to M to run, and M is the person who actually does the work

P: takes the first letter of processor to provide context for M’s execution and stores resources, such as the locally runnable G queue and memeory cache, that M uses to execute G.

“Are you familiar with the GMP concurrent model? “

GM old scheduler:

  1. Fierce lock competition
    • Get G from global queue, need to lock
  2. Poor locality
    • For example, when G contains the creation of a new coroutine, M creates G ‘, and in order to continue executing G, G ‘needs to be handed over to M’, which also causes poor locality, because G ‘is related to G, and it is better to execute on M rather than other M’.
  3. High system overhead
    • System calls (CPU switching between M’s) lead to frequent thread blocking and unblocking operations that add overhead.

If M wants to execute or put G back, it must access the global G queue, and there are multiple M, that is, multiple threads accessing the same resource need to lock to ensure mutual exclusion/synchronization, so the global G queue is protected by a mutex.

New VERSION of GMP scheduler (memory diagram -GMP)

  1. Fixed old GM scheduler issues

  2. M(thread):N(coroutine) relation

    • Create M threads (the unit of CPU execution scheduling), and the subsequent N goroutines will be attached to these M threads.

    • Only one Goroutine can run on a thread at a time. When a Goroutine is blocked, runtime dispatches the current Goroutine and lets another Goroutine execute it.

  3. Work Stealing

    • If there is no G in a global queue, M will need to perform work stealing: stealing half of G from other P’s that have G and putting it into its own P’s local queue
  4. Hand off the right to execute

    • When a G blocks, the thread releases the bound P and transfers the P to another idle thread

“Go func() execution process?”

  1. The go keyword creates a goroutine queue and enlists the global G queue if the local P queue is full
  2. From G at the head of queue P to M
  3. P has two key properties
    1. work stealing
    2. hand off

Check out Github for more