Translation Go Scheduler

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

There have been some adjustments in recent work, so it took almost two weeks for this article to be translated. Think about the industry and work that @Fenng told me a few years ago, although the starting point is not quite the same, but the conclusion is true!

The change in work is not a lot of nonsense. The "Go Scheduler" in the original.

———— Translation Split Line ————

Go Scheduler

Daniel morsing

Overview

One of the important features of Go 1.1 is the new scheduler, which is contributed by Dmitry Vyukov. Without any adjustment to the program, the new scheduler can bring an exciting performance boost to the Go program. So I think it's necessary to write something about it.

Most of the content described in this blog post has been described in the original design documentation. It's a fairly comprehensive document, and it's also quite professional.

Everything you want to know about the new scheduler can be found in that document, and this post depicts the overall situation, so it's a little bit better.

Why the Go runtime requires a scheduler

Before you know the new scheduler, learn why you need it. Why you need to create a user space scheduler when the operating system has been able to schedule threads.

The POSIX threading API is definitely a logical extension of the existing Unix process model, so that threads have a similar way of controlling the process. Threads have their own signal masks that can be associated with the CPU and can be put into cgroups or queried for which resources are being used. All of the features of these controls are not required for the Go program using Goroutine, and the required control expands rapidly when the program has 100,000 threads.

Another problem is that the OS cannot dispatch based on the GO model according to the actual situation. For example, the Go garbage collector requires all threads to be stopped first while the memory must be in a consistent state when performing a collection. This includes where the running thread is waiting to execute to a known memory that will reach a consistent state.

When there are many threads that are randomly scheduled, the challenge is that you have to wait for them to reach a consistent state. The Go scheduler can decide where the known memory will be aligned. This means that when the garbage collection is stopped, only the threads that are actually running on the CPU core need to wait.

Our lineup

There are typically three threading models. One is N:1, which is a number of user-space threads running on an OS thread. The advantage is that context switches are very fast, and the downside is that you can't play multicore systems. The other is 1:1, which is an execution thread that corresponds to an OS thread. The advantage is that you can take advantage of all the cores on the machine, but because it is done through the OS, context switching is very slow.

Go attempts to use the M:N scheduler to find a balance between the two worlds. Several goroutine are scheduled on several OS threads. Get a quick context switch and can take advantage of all the cores in the system. The main problem is that this method increases the complexity of the scheduler.

To complete the task scheduling, the GO Scheduler uses 3 main entities:

A triangle represents an OS thread. It is a thread that is executed by system administration and works quite like a standard POSIX thread. In the run-time code, called M represents the device.

The circle represents Goroutine. It includes the stack, instruction pointers, and other important information needed to dispatch the goroutine, such as any channel that might block it. In run-time code, it is called G.

The rectangle represents the context of the dispatch. It can be thought of as a local version of the scheduler that runs the Go code on one thread. This is an important link from the evolution of the N:1 Scheduler to the M:N scheduler. In runtime code, it is called P to represent the processor. I have to say a few more words about this part.

There are 2 threads (M), each with a context (P), each executing a goroutine (G). The thread must have a context in order to execute goroutine.

The number of contexts is set by the environment variable Gomaxprocs at startup, or by the run-time function Gomaxprocs (). In fact the number of contexts is fixed, which means that only Gomaxprocs Go code is executed at any one time. You can use this to make adjustments on different computers, such as a 4-core PC that runs 4 Go-code threads.

The gray Goroutine is not running, but is ready to be dispatched. They are arranged in a list called runqueues. When Goroutine executes the GO statement, it is added to the tail of runquque. When a running goroutine arrives at the dispatch point, the context pops the Goroutine from Runqueue, sets the stack and instruction pointers, and starts the next goroutine.

To reduce mutex contention, each context has its own local runqueue. A previous version of the Go Scheduler has only one global runqueue that is protected with a mutex. Threads are often blocked in order to wait for the mutex to be unlocked. This can become very bad when you want to squeeze as much performance as possible on a 32-core machine.

As long as the context has goroutine need to run, the scheduler will be in this stable state of continuous scheduling. However, there are some situations that may change the situation.

Who do you want to call (System)?

Now you might be wondering why you need a context? Why can't I just put runqueue on the thread without leaving the context? It's not really like that. The reason for the context is that you can switch to another thread when the thread being executed is blocked for some reason.

An example of blocking is a system call. Because the thread cannot block the system call while it is running the code, the context must be toggled to ensure dispatch.

Here you can see that a thread has abandoned its context, so other threads can run. The scheduler guarantees that there are enough threads to run all the contexts. In order to properly handle system calls, M1 is created or fetched from the thread cache. Technically, the goroutine that is held by the system call thread is still running, although it is blocked at the OS level.

When the system call returns, the thread must try to get the context so that Goroutine continues to run. The usual pattern is to steal a context from another thread. If there is no way to steal it, it will put Goroutine into the global runqueue and then return to the thread cache itself to continue hibernation.

When the context finishes executing the local runqueue, the Goroutine is obtained from the global Runqueue. The context also periodically checks the global runqueue. Otherwise, the goroutine on the global runqueue may never run because of a lack of resources.

This method of handling system calls illustrates why the Go program runs multiple threads even when Gomaxprocs is 1. The runtime invokes the system call with Goroutine and lets the thread hide behind.

Stealing jobs

The stable state of the system also changes when a context scheduler executes all goroutine. This occurs when the context of the Runqueue assignment is unbalanced. This causes the work to be done in the system after the context empties its runqueue. In order for the go code to continue executing, the context can get goroutine from the global runqueue, but if there is no goroutine in it, it must be obtained from somewhere else.

This other place is actually the other context. When a context executes, it tries to steal half the runqueue of the other context. This ensures that each context always has the work to do and that all the threads are in their maximum ability to work.

Go?

There are also many scheduler details, such as the CGO thread, the Lockosthread () function, and the instructions with the network pool. They are not within the scope of this article, but they are still worth learning. I'll probably write something about that later. There are many interesting creations waiting to be explored in the Go runtime library.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.