Home  >  Article  >  Backend Development  >  Goodbye Go interviewer: GMP model, why is there P?

Goodbye Go interviewer: GMP model, why is there P?

Golang菜鸟
Golang菜鸟forward
2023-08-08 16:31:451363browse


Today’s protagonist is an extension question (question) of the all-purpose GMP model question in the Go interview, that is "GMP model, why is there P ? "

Further deliberation behind the question, in fact, the essence of this interview question is to ask: "GMP model, why can't G and M be directly bound? There is also an additional P. It's so troublesome, why is it, what problem is it trying to solve? "

In this article, Jianyu will take you to explore the reasons for the changes in GM and GMP models.

GM model

Before Go1.1, Go’s scheduling model was actually the GM model, that is, there was no P.

Today I will take you to review past designs.

Decrypting the Go1.0 source code

One of the ways we can understand something is to look at the source code, and look at Go1.0.1 with Jianyu The core key steps of the scheduler source code:

static void
schedule(G *gp)
{
 ...
 schedlock();
 if(gp != nil) {
  ...
  switch(gp->status){
  case Grunnable:
  case Gdead:
   // Shouldn't have been running!
   runtime·throw("bad gp->status in sched");
  case Grunning:
   gp->status = Grunnable;
   gput(gp);
   break;
  }

 gp = nextgandunlock();
 gp->readyonstop = 0;
 gp->status = Grunning;
 m->curg = gp;
 gp->m = m;
 ...
 runtime·gogo(&gp->sched, 0);
}

  • Call the schedlock method to obtain the global lock.
  • After successfully acquiring the global lock, change the current Goroutine state from Running (being scheduled) to Runnable (can be scheduled).
  • Call the gput method to save the current Goroutine running status and other information for subsequent use.
  • Call the nextgandunlock method to find the next runnable Goroutine and release the global lock for other schedulers to use.
  • After obtaining the next Goroutine to be run, change its running status to Running.
  • Call the runtime·gogo method to run the next Goroutine to be executed just obtained and enter the next round of scheduling.

Thinking about the GM model

By analyzing the scheduler source code of Go1.0.1, we can find a more interesting point. That is the scheduler itself (schedule method). Under normal processes, it will not return, that is, it will not end the main process.

Goodbye Go interviewer: GMP model, why is there P?
G-M model diagram

He will continuously run the scheduling process. After GoroutineA is completed, it will start looking for GoroutineB. When B is found, it will The completed scheduling right of A is handed over to B, allowing GoroutineB to start being scheduled, that is, running.

Of course, there are also Gs that are blocked (Blocked). Suppose G is making some system or network calls, which will cause G to stall. At this time, M (system thread) will be put back in the kernel queue, waiting for a new round of wake-up.

Disadvantages of the GM model

On the surface, the GM model seems to be indestructible and flawless. But why change it?

In 2012, Dmitry Vyukov published the article "Scalable Go Scheduler Design Doc", which is still the main target of major research articles on the Go scheduler. He described the overall reasons and considerations in the article. The following content refers to this article.

The current Goroutine scheduler (referring to the GM model of Go1.0) limits the scalability of concurrent programs written in Go, especially high-throughput servers and parallel computing programs.

The implementation has the following problems:

  • There is a single global mutex (Sched.Lock) and centralized state management:
    • mutex needs to protect all goroutine-related operations (creation, completion, reordering, etc.), resulting in serious lock competition.
  • Goroutine passing problem:
    • goroutine (G) handover (G.nextg): worker thread ( Runnable goroutines are frequently handed over between M's).
    • The above may result in increased latency and additional overhead. Every M must be able to execute any runnable G, especially the M that just created G.
  • Each M needs to do memory caching (M.mcache):
    • will lead to excessive resource consumption (Each mcache can absorb 2M of memory cache and other caches), data locality is poor.
  • Frequent thread blocking/unblocking:
    • In the presence of syscalls, threads are often blocked and unblocked block. This adds a lot of extra performance overhead.

GMP model

In order to solve the above many problems of the GM model, in Go1.1, Dmitry Vyukov worked on the GM model Based on this, a new P (Processor) component is added. And implemented the Work Stealing algorithm to solve some newly generated problems.

Goodbye Go interviewer: GMP model, why is there P?

GMP model, in the previous article "Go group friends asked: What is the appropriate number of Goroutines to control, will it affect GC and scheduling? has been explained in ".

# Friends who think it’s good can pay attention to it, I won’t repeat it here.

What changes will it bring

What changes will it bring after adding P? Let’s talk about it more explicitly.

  • Each P has its own local queue, which greatly reduces the direct dependence on the global queue. The result is a reduction in lock competition. The bulk of the performance overhead of the GM model is lock competition.

  • On the relative balance of each P, the Work Stealing algorithm is also implemented in the GMP model. If the local queue of P is empty, it will be removed from the global queue. Or steal the runnable G from the local queue of other P to run, reducing idling and improving resource utilization.

Why is there P

At this time, some friends may be confused. If you want To implement the local queue and Work Stealing algorithm, why not add it directly to M? M can still achieve similar functions. Why add another P component?

Combined with the positioning of M (system thread), if you do this, there are the following problems.

    Generally speaking, the number of M will be more than P. Like in Go, the maximum limit of the number of M is 10000, and the default number of P is the number of CPU cores. In addition, due to the properties of M, that is, if there is a system blocking call that blocks M and is not enough, M will continue to increase.
  • If M continues to increase, if the local queue is mounted on M, it means that the local queue will also increase accordingly. This is obviously unreasonable, because the management of local queues will become complicated and Work Stealing performance will be significantly reduced.
  • #After M is blocked by a system call, we hope to allocate its unexecuted tasks to others to continue running, rather than causing everything to stop as soon as it is blocked.
  • Therefore, it is unreasonable to use M. Then introducing a new component P and associating the local queue with P can solve this problem well.

SummaryToday’s article combines some historical situations, cause analysis and solution description of the entire Go language scheduler.

"GMP model, why is there P?" This question is like a system design understanding, because now many people will memorize the GMP model or go through it in an instant style in order to cope with the interview. And understanding the real reasons behind it is what we need to learn and understand.

Only by knowing what is happening and why is it happening can we break the situation.

The above is the detailed content of Goodbye Go interviewer: GMP model, why is there P?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:Golang菜鸟. If there is any infringement, please contact admin@php.cn delete
Previous article:Go Basics GoroutineNext article:Go Basics Goroutine