If you have any questions or suggestions, please contact us in time. My official account is “Brain fried Fish”, GitHub address: github.com/eddycjy.
Hello, I’m fried fish.
A few days ago, I saw a friend in the reader exchange group, who sent out a fatal question, that is: “How many goroutine is appropriate for a single machine?” .
Your first response may be the same as that of someone in your group: “I’m not sure how much to control.”
Then there is a further puzzle: “Too many Goroutines will affect GC and scheduling, mainly how to budget this number is reasonable?”
This is the subject to be discussed in this paper, so the structure of this paper will be to explore the basic knowledge first, and then reveal step by step to deeply understand this problem.
What is the Goroutine
One of the favorite features of Go, a nascent programming language, is Goroutine. The Goroutine is a lightweight thread managed by the Go runtime, commonly referred to as a “coroutine.”
go f(x, y, z)
Copy the code
The operating system itself is not aware of the existence of Goroutine, Goroutine operations and switches belong to the “user mode.”
Goroutine is controlled by a specific scheduling pattern and runs “multiplexed” on several system threads assigned by the operating system to the Go program.
The overhead of creating a Goroutine at the same time is minimal, requiring only 2-4K of stack space initially. The Goroutine itself will self-scale according to actual use and is very lightweight.
func say(s string) { for i := 0; i < 9999999; {time.sleep (100 * time.millisecond) FMT.Println(s)}} func main() {go say(" hello ") say(" hello ")}Copy the code
Can open hundreds of millions of coroutine bully, is one of the Go language’s glory.
What is scheduling
Now that Goroutine represents the user state, the operating system can’t see it. There must be something to manage it in order for it to work better.
This refers to scheduling in the Go language, the most common and interview favorite GMP model. Therefore, the following will introduce the basic knowledge and process of Go scheduling.
The following is excerpted from a chapter in “A Journey to Programming the Go Language” by Fried Fish and God P.
Scheduling basics
The Go Scheduler dispenses runnable Goroutines to OS threads running on the processor. The scheduler is often referred to in three acronyms:
- G: Goroutine, actually every time we call it
go func
I just generated a delta G. - P: Processor. Generally, the number of P is the number of Processor cores
GOMAXPROCS
Modify. - M: System thread.
These three interactions actually come from the M: N scheduling model of Go. That is, M must bind to P, and then constantly loop over M looking for a runnable G to perform the corresponding task.
Scheduling process
We made a simple analysis with the flow chart of the GMP model. The official figure is as follows:
- When we execute
go func()
“, essentially creating a brand new Goroutine, which we’ll call G. - The newly created G is placed in the Local Queue or Global Queue of P to prepare for the next action. One thing to notice is that the P here refers to the P that created G.
- Wake up or create M to execute G.
- Continue the loop of events
- Find G in the available state to execute the task
- After clearing, re-enter the event loop
As mentioned in the description, both global and local queues are functionally used to store G waiting to be run, but the difference is that the number of local queues is limited to 256.
In addition, when G is created, the local queue of P is preferentially selected. If the local queue is full, half of G of the local queue of P is moved to the global queue.
This can be understood as scheduling resource sharing and rebalancing.
Stealing is
We can see the steal behavior on the graph, what is it used for? We all know that when you create a new G or when the G becomes runnable, it is pushed to the local queue of the current P.
In fact, when P has finished executing G, it will also “work”, it will eject G from the local queue, it will check whether the current local queue is empty, if so, it will randomly try to steal half of the runnable G from other P’s local queue to its own name.
The official picture is as follows:
In this example, P2 cannot find any G in the local queue to run on, so it will execute the work-stealing scheduling algorithm to randomly select other processors P1 and steal three GS from P1’s local queue into its own local queue.
At this point, BOTH P1 and P2 have runnable G, and the excess G of P1 will not be wasted. Scheduling resources will be more evenly circulated among multiple processors.
Are there any restrictions
In the previous section, we gave a basic introduction and share of the Go scheduling model and Goroutine.
Then we went back to the theme of “Too many Goroutines, does that matter?”
After understanding the basic knowledge of GMP, we need to know in the coroutine running process, the real work of GPM and what constraints?
Fry fish with us from the GMP step by step analysis.
M’s limit
First, which of the GPMS does the real work in the execution of coroutines?
It must be M (system thread), because G is something in user mode, and the final execution has to be mapped to M (system thread).
So is there a limit on M?
The answer is: yes. In the Go language, the default limit for the number of M is 10000, and an error will be reported if it is exceeded:
GO: runtime: program exceeds 10000-thread limit
Copy the code
This is usually only encountered in the case of a blocking operation in Goroutine. It could also be a sign of a problem with your program.
If you really need that many, you can also set them with the debug.setMaxThreads method.
G the limits of
Second, what about G? Is there a limit to how many goroutines can be created?
The answer is: no. But it is theoretically affected by memory, assuming a Goroutine creation requires 4K (via @gowkh) :
- 4K * 80,000 = 320,000 K ≈ 0.3g memory
- 4K * 1,000,000 = 4,000,000 K ≈ 4G memory
This gives a relative calculation of the number of levels of goroutines that can be created on a single machine in the ordinary world.
Note: Goroutine creates the required 2-4K applications that require contiguous memory blocks.
The limitation of P
Third, what about P, is there a limit on the number of P’s? How does that affect them?
The answer is: yes. The quantity of P is directly influenced by the environment variable GOMAXPROCS.
What is the environment variable GOMAXPROCS? In Go, you can set GOMAXPROCS to adjust the number of PROCESSORS (P) in a scheduling process.
Another important point is that M (system thread) associated with P needs to bind P to perform specific tasks, so the amount of P will affect the performance of Go program.
The number of P is basically affected by the kernel number of the machine, so there is no need to worry too much about it.
Does the number of P’s affect the number of goroutines created?
The answer is: no. And if you have more goroutines and fewer goroutines, you can just do what you want with P without causing a catastrophic problem.
What makes sense
After the introduction of GMP’s respective limitations, we come back to the point of “How can Goroutine quantity be reasonably budgeted?” .
The word “reasonable” needs to be defined according to specific scenarios, which can be combined with the above learning and understanding of GPM. It is concluded that:
- M: Yes, the default limit is 10000, adjustable.
- G: There is no limit, but it is affected by memory.
- P: affected by the number of cores on the machine, can be large or small, does not affect the number of G creation.
The quantity of Goroutine is below the controllable limit of MG, so a few or dozens of Goroutine can be called “reasonable” because it has no effect.
Real situation
In a real application scenario, this definition is not so simple. If you Goroutine:
- With frequent requests for HTTP, MySQL, open files, etc., it is certainly not reasonable to assume that hundreds of thousands of coroutines are running in a short period of time (possibly causing too many files to open).
- Common Goroutine leaks cause CPU, Memory, etc., depending on what is running in your Goroutine.
It depends on what’s running in the Goroutine.
conclusion
In this article, the basic knowledge of Goroutine, GMP and scheduling model is introduced respectively, and the following problems are expanded:
- What is the appropriate number of Goroutines for a single machine?
- Too many Goroutines will affect GC and scheduling, mainly how to budget this number is reasonable?
The number of goroutines on a single machine can be considered “reasonable” as long as it is below the limit.
Depending on what you’re running, running a resource monster can kill you if you only run a few Goroutines. So if you want to define a budget, it depends on what you’re running.
My official account
Share Go language, micro service architecture and strange system design, welcome to pay attention to my public number and I exchange and communication.
The best relationship is mutual achievement. Your praise is the biggest motivation for the creation of fried fish. Thank you for your support.