Translation Go Language Scheduler

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

Go Language Scheduler

Order of Translation

This article translates Daniel morsing's blog, the Go scheduler. Personally think this article is easy to understand the knowledge of go routine and scheduler. As an introductory article. Very good.

President

Introduced

One of the biggest features of the Go 1.1 version is a new scheduler, contributed by Dmitry Vyukov.

This new scheduler brings an exciting, non-successor performance boost to the parallel Go program. I think I should write something for it.

Most of the content of this blog has been described in this original design document, which is a fairly well understood article. But a little technical.

Although the design document already includes everything you need to know about the new scheduler. But this blog post includes pictures, so it's very obvious that it notch above.

Why a scheduler is required for go execution

Before we look at this new scheduler, we need to figure out why we need it and why we need to create a user-space scheduler. Even though the operating system has been able to dispatch threads for you.

The POSIX threading API is largely a logical extension of the existing UNIX process model, with many threads having the same control over the process. Threads have their own signal masks, can set CPU affinity, can be grouped into cgroups, and can query which resources they use.

All of these controls add overhead due to some features that the go language does not need when using goroutine, and the overhead is stacked up when your program has 100,000 threads.

Another problem is that the operating system cannot make notification of scheduling decisions, based on the go model. For example, the go garbage collector needs to be stopped at the time of collection, and the memory needs to be in a consistent state.

This involves waiting for the executing thread to reach the point where we know the memory is consistent.

When you have very many threads that need to be randomly dispatched, there is a very large possibility that you need to wait for many threads to reach a consistent state.

The Go Scheduler can make decisions by scheduling only when he knows that the memory is consistent.

This means that when we stop because of garbage collection, we just need to wait for those threads that are executing in the CPU core.

Our role

Threads typically have 3 models: one is the N:1 model , and multiple user threads in the model are executed in a kernel thread. The advantage of such a model is that context switching is very fast, but it is not possible to take full advantage of multicore threads. There is also a 1:1 model, in which an execution thread corresponds to a system thread. The model takes full advantage of multiple cores on the machine, but the context switch is very slow due to the need to fall into the kernel.

Go uses the M:N model to try to take advantage of both. It dispatches an arbitrary number of user threads on a random number of system threads, so you can not only get a very fast context switch, but also take advantage of the multicore of your system. The main disadvantage of this approach is the complexity that is brought to the scheduler.

To complete a scheduled task, the Go Scheduler uses 3 entities:

A triangle represents a system thread, which is managed by the operating system and behaves much like a POSIX thread. In code execution, it is called M(machine).
A circle represents a goroutine. It includes stacks, instruction pointers, and other information that is important for scheduling goroutine, such as its blocked channel. In the execution-time code. Called G.
The rectangle represents the context of the dispatch.

You can think of it as the local version number of the scheduler that executes the go code in a single thread.

It is an important part of dispatching from N:1 to m:n. In the execution-time code. Called P(Processor, processor).

In the figure we see 2 threads (M), each of which holds a context (P). Each context is executed with a goroutine (G). In order to execute goroutines, each thread must hold a context.

The number of contexts is set to the value of the environment variable at startup GOMAXPROCS , or by calling the function at execution time GOMAXPROCS() .

In general, this value does not change during program execution. A fixed number of contexts means that only GOMAXPROCS one thread is executing the go code at random times.

We can use this to adjust the call of the go process to different machines. For example, on a 4-core CPU with 4 threads to execute the go code.

The gray Goroutine is not in the execution, but waits to be dispatched. They are arranged in a runqueues list called. When an goroutine executes an go expression, Goroutines is added to the end of the list. When a context executes goroutine to the dispatch point, it pops a goroutine from its runqueues. Set the stack and instruction pointers, and then start executing the goroutine.

In order to break the mutual exclusion, each context has its own local runqueues.

The Go scheduler of the previous version number only has a global runqueues and a mutually exclusive lock to protect it. Threads are often blocked, waiting for mutually exclusive locks to release contact jams. Assuming that your machine has 32 cores, this will become very inefficient.

Just to have goroutine can execute all the contexts, the GO Scheduler will dispatch according to this stable state.

However, there are several exceptions to this situation.

Who do you want to call (System)?

Now you may wonder, why do you want to go to the following? Can we not abandon the context and put runqueues directly on the thread? Not really We need context because we are able to give the context to other threads when the thread in the current execution needs to be plugged.
One example of a blockage is when we make a system call. Because threads cannot execute code and plug in system calls, we need to transfer the context. To proceed with the dispatch.



We can see. A thread abandons its context so that other threads can execute it. The scheduler guarantees that there are enough threads to perform all the contexts. The M1 in the illustration may have just been created to handle this system call. Or it comes from the thread cache.

The thread that executes the system call continues to hold the goroutine that generated the system call. Since it is technically thrown in execution, it is simply clogged up in the operating system.

When the system call returns. The thread must try to get the context. To continue executing the returned goroutine. The usual mode of operation steals a context from another thread.

Assuming a steal fails, it puts goroutine into the global Runqueue, puts itself into the thread cache and sleeps.

When the local runqueue of the context is empty, the global runqueue is pulled. The context also periodically checks the global runqueue, otherwise the goroutine in global runqueue may never be executed and finally starve to death.

Stealing jobs

The stable state of the system changes in another case when the runqueue of one of the contexts is empty. No Goroutine can dispatch. This can occur in the case of runqueues imbalance between contexts.

This can cause the context to run out of its runqueue and the system still has work to complete.

In order to continue executing the Go code, the context can get goroutine from the global runqueue, but assuming there is no goroutines, the context needs to be fetched from somewhere else.

The so-called other places are other contexts.

When a context consumes its goroutines, it steals half the goroutine from another context.

This ensures that every context always has work to do, ensuring that all threads are working with their greatest ability.

Related Article

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.