Backend Development 10 min read

Understanding Go's Scheduler: From the GM Model to the Efficient GMP Design with Work‑Stealing

This article explains the evolution of Go's scheduler, detailing the early GM model, its limitations, and the modern GMP design with processors, local and global runnable queues, and work‑stealing, illustrating how these mechanisms enable high‑performance concurrent programs.

IT Services Circle
IT Services Circle
IT Services Circle
Understanding Go's Scheduler: From the GM Model to the Efficient GMP Design with Work‑Stealing

Go's greatest appeal is that a simple go keyword can launch a goroutine, allowing developers to write high‑concurrency server applications without dealing with OS‑level scheduling details. Understanding the Go scheduler’s mechanisms helps write performant concurrent code and improves debugging and optimization skills.

1. Introduction

The article begins by highlighting the convenience of creating goroutines with the go keyword and sets the stage for a deeper dive into the scheduler.

2. Process, Thread, Coroutine

Any concurrent program ultimately runs under the operating system, involving three core concepts:

Process : the OS resource allocation unit.

Thread : the CPU scheduling unit; each thread’s stack is about 1 MB .

Coroutine (in Go, a goroutine ): a lightweight user‑space thread. When a goroutine is created its initial stack is only 2 KB , demonstrating its lightweight nature.

3. Early Scheduling Model – GM Model

Before version 1.1, Go used a simple GM model consisting of two structures:

G (Goroutine): created by go func() .

M (Machine): the OS thread that executes Go code.

The GM model relied on a single global goroutine queue, causing several problems under high concurrency:

1. A single global mutex (Sched.Lock) protects all goroutine operations, creating lock contention. 2. Frequent hand‑offs of runnable goroutines between Ms increase latency. 3. Per‑M memory caches are allocated even for Ms blocked in syscalls, wasting resources. 4. Aggressive thread blocking/unblocking adds overhead.

4. Efficient GMP Scheduling Model

To address the inefficiencies of the GM model, Go introduced a middle layer – the P (Processor). A P manages a set of goroutines and decouples the M (OS thread) from the G (goroutine).

P is the processor that holds runnable goroutine queues.

The new design adds two queues:

LRQ (Local Runnable Queue): attached to a specific P.

GRQ (Global Runnable Queue): shared across all Ps.

When a new goroutine is created it is first placed on the current P’s LRQ. If the LRQ is full, the goroutine is enqueued in the GRQ.

The runtime scheduling loop (simplified) looks like:

runtime.schedule() {
    // only 1/61 of the time, check the global runnable queue for a G.
    // if not found, check the local queue.
    // if still not found, try to steal from other Ps.
    // if still not found, check the global runnable queue again.
    // if still not found, poll network.
}

Key steps:

If GRQ has a goroutine (rare, ~1/61), it is taken.

Otherwise, the scheduler checks the LRQ of the current P.

If LRQ is empty, the P attempts to steal half of the goroutines from a randomly chosen other P (work‑stealing).

If stealing fails, GRQ is checked again.

Finally, if no runnable goroutine exists, the Network Poller is consulted to handle I/O‑blocked goroutines.

Work‑Stealing Mechanism

When a new G is created, it is appended to its P’s LRQ tail. After a G finishes, its P first tries to pop another G from its own LRQ; if empty, it randomly selects another P and steals roughly half of that P’s runnable goroutines. This reduces contention and improves locality.

5. Summary

The article covered the evolution of Go’s scheduler from the simple GM model to the modern GMP design, emphasizing how the introduction of P, LRQ/GRQ, and work‑stealing dramatically improve concurrency performance. This is the first piece in a series on Go concurrency programming.

References

Head First of Golang Scheduler – https://zhuanlan.zhihu.com/p/42057783

Why P exists in the GMP model – https://juejin.cn/post/6956008643456139301

Golang Scheduler GMP analysis – https://learnku.com/articles/41728

Go's work‑stealing scheduler – https://rakyll.org/scheduler/

Scheduling In Go : Part II – https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part2.html

Goroutine scheduling overview – https://zhuanlan.zhihu.com/p/24405494

backendConcurrencyGoSchedulergoroutineWorkStealing
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.