当前位置:网站首页>What is the GPM scheduler for go?

What is the GPM scheduler for go?

2022-07-04 00:27:00 Li Jue

  There are seven common concurrency models :

  • Threads and locks

  • Functional programming

  • Clojure Road

  • actor

  • Communication sequence process (CSP)

  • Data level parallelism

  • Lambda framework

Go Concurrent model of -CSP

Go The concurrent synchronization model of the language comes from a process called communication sequence (Communicating Sequential Processes,CSP) The paradigm of (paradigm), The core idea is to pass two concurrent entities through the channel channel Connect , All messages passed channel transmission ..CSP It's a messaging model , By means of goroutine Passing data between to pass messages , Instead of locking the data for synchronous access . Used in goroutine The key data types that synchronize and transfer data between are called channels (channel).

Go The scheduling model of -GPM

that CSP And Go What does language matter ? So let's see Go Language pairs CSP Implementation of concurrency model ——GPM Scheduling model .

 

GPM Scheduling model

GPM It represents three roles , Namely Goroutine、Processor、Machine.

  • Goroutine: It's what we usually use go Keyword created execution , It corresponds to a structure g, The structure is preserved goroutine Stack information for

  • Machine: Represents the thread of the operating system

  • Processor: Represents a processor , management G and M The connection of

Goroutine

Goroutine Is used in the code go Keyword created execution unit , We are also familiar with “ Lightweight threads ” It is called the synergy , Coroutines is Unknown to the operating system , It is implemented at the programming language level , Context switching does not need to go through kernel mode , In addition, the memory space occupied by the coroutine is very small , So it has great development potential .

go func() {}()

stay Go In language ,Goroutine By a person named runtime.go The structure of , This structure is very complex , Yes 40 Multiple member variables , It mainly stores the execution stack 、 state 、 Currently occupied threads 、 Scheduling related data . But I'm sorry , Officials consider Go The development of language , Set to private , I won't call you .

type g struct {
 stack struct {
  lo uintptr
  hi uintptr
 }        //  Stack memory :[stack.lo, stack.hi)
 stackguard0 uintptr
 stackguard1 uintptr

 _panic       *_panic
 _defer       *_defer
 m            *m    //  Current  m
 sched        gobuf
 stktopsp     uintptr  //  expect  sp  Stack top , Used for retrospective inspection 
 param        unsafe.Pointer // wakeup  Parameters passed during wakeup 
 atomicstatus uint32
 goid         int64
 preempt      bool        //  Grab the signal ,stackguard0 = stackpreempt  Copy of 
 timer        *timer         //  by  time.Sleep  Cached timer 

 ...
}

Goroutine Scheduling related data is stored in sched, Switch in coordination 、 When restoring the context .

type gobuf struct {
 sp   uintptr
 pc   uintptr
 g    guintptr
 ret  sys.Uintreg
 ...
}

Machine

M Is the thread corresponding to the operating system , There will be at most GOMAXPROCS An active thread can run normally , By default GOMAXPROCS Is set to the number of cores , Suppose there are four cores , Then four threads are created by default , Each thread corresponds to one runtime.m Structure . The number of threads is equal to CPU The reason for the number is , Each thread is assigned to one CPU There will be no context switching of threads , The system overhead can be minimized .

type m struct {
 g0   *g 
 curg *g
 ...
}

M There are two important things in it , One is g0, One is curg.

  • g0: Will be deeply involved in the scheduling process at runtime , such as goroutine The creation of 、 Memory allocation, etc

  • curg: Represents the currently executing goroutine.

Just said P Be responsible for M And G The associated , therefore M There is also storage and P Relevant data .

type m struct {
  ...
 p             puintptr
 nextp         puintptr
 oldp          puintptr
}
  • p: The processor that is running the code

  • nextp: Temporary processor

  • old: The processor of the thread before the system call

Processor

Proccessor be responsible for Machine And Goroutine The connection of , It can provide the context required by threads , Can also be allocated G Execute on the thread it should go , With it , Every G Can be reasonably called , Every thread is no longer fishing in troubled waters , It's really a must-have at home .

alike , The number of processors also defaults to GOMAXPROCS To set up , It corresponds to the number of threads one by one .

type p struct {
 m           muintptr

 runqhead uint32
 runqtail uint32
 runq     [256]guintptr
 runnext guintptr
 ...
}

Structure P Performance tracking is stored in 、 Garbage collection 、 Timer and other related fields , It also stores the waiting queue of the processor , What is stored in the queue is to be executed Goroutine list .

The relationship among the three

First , By default, four threads and four processors are started , Then bind to each other .

This is the time , One Goroutine The structure is created , In the process of function body address 、 Parameter start address 、 After the parameter length and other information and scheduling related attributes are updated , It will enter a processor queue and wait for the departure .

what , Another G? Then take turns to other P Put it inside , I'm sure you'll pass when you see no one queuing at other windows when you wait in line to get your number .

 

If there are many G, What if it's full ? Then don't G Into the private queue of the processor , Instead, put it in the global queue ( Waiting hall ).

 

In addition to pushing it in ,M This side still needs to take out crazily , First, go to the private queue of the processor to get G perform , If it is finished, go to the global queue to get , If not in the global queue , Just steal from the queue of other processors , wow , So hungry , It's a demon !

 

If nothing is found to be implemented G Well ? that M Will be too disappointed and P Disconnect , Then go to bed (idle) 了 .

 

If there are two Goroutine Passing channel How to do something that blocks you , Don't M Wait until they are finished before continuing ? Obviously not. ,M This pair is not rare Go men and women , And will turn around to find something else G perform .

 

system call

If G Made a system call syscall,M It will also enter the system call state , So this P It's a waste to stay here , What shall I do? ? The subtlety of this is ,P Will not wait foolishly G and M System call complete , Instead, I will find others who are relatively idle M Perform other G.

 

When G Completed the system call , Because we need to continue to implement , So we must find another idle processor to start .

 

If there is no idle processor , Then you can only G Put it back in the global queue for allocation .

 

sysmon

sysmon It's our cleaning aunt , It's a M, Also called monitoring thread , Unwanted P Can run independently , Every time 20us~10ms Will be awakened and come out to clean up , The main job is to recycle garbage 、 Recover the long-time system scheduling blocking P、 To run for a long time G Send preemptive scheduling, etc .







Reference resources :Go Linguistic GPM What is a scheduler ?



In the figure 6-2 in , You can see the operating system thread 、 The relationship between the logical processor and the local run queue . If you create a goroutine And get ready to run , This goroutine It will be put into the global running queue of the scheduler . after , The scheduler will send the goroutine Assign to a logical processor , And put it into the local running queue corresponding to the logical processor . Run... In the queue locally goroutine Will be waiting , Until the logic processor assigned to it executes .

Sometimes , Running goroutine You need to make a blocking system call , If you open a file . When such a call occurs , Threads and goroutine Will be detached from the logical processor , The thread will continue to block , Wait for the return of the system call . meanwhile , The logical processor loses the thread it uses to run . therefore , The scheduler will create a new thread , And bind it to the logical processor . after , The scheduler will choose another... From the local run queue goroutine To run the . Once the blocked system call is executed and returned , Corresponding goroutine It will be put back in the local run queue , And the previous thread will be saved , So that we can continue to use .

If one goroutine Need to make a network I/O call , There will be some differences in the process . under these circumstances ,goroutine Will be separated from the logical processor , And move to the runtime with the integrated network poller . Once the poller indicates that a network read or write operation is ready , Corresponding goroutine It will be reassigned to the logical processor to complete the operation . The scheduler has no limit on the number of logical processors that can be created , But by default, the language runtime limits each program to create at most 10000 Threads . This limit can be set by calling runtime/debug Bag SetMaxThreads Method to change . If the program tries to use more threads , It will collapse .

Concurrent (concurrency) It's not parallel (parallelism). Parallel is to let different code segments execute on different physical processors at the same time . The key to parallel is to do a lot of things at the same time , and Concurrency means managing many things at the same time , These things may be done only half of the time is suspended to do other things . In many cases , Concurrency is better than parallelism , Because the total resources of the operating system and hardware are generally very small , But it can support the system to do many things at the same time . such “ Use fewer resources to do more ” Philosophy of , It's also guidance Go Philosophy of language design .

If you want to let goroutine parallel , More than one logical processor must be used . When there are multiple logical processors , The scheduler will goroutine Equally allocated to each logical processor . It will make goroutine Running on different threads . But if you really want to achieve the parallel effect , Users need to run their programs on machines with multiple physical processors . otherwise , Even if the Go The language runtime uses multiple threads ,goroutine Still running concurrently on the same physical processor , Not parallel .

chart 6-3 Shows how to run concurrently on a logical processor goroutine And running two concurrent on two logical processors in parallel goroutine The difference between . The scheduler contains some clever algorithms , These algorithms will follow Go The release of the language has been updated and improved , Therefore, it is not recommended to blindly modify the default settings of the language runtime for the logical processor . If you really think that changing the number of logical processors can improve performance , You can also fine tune the parameters of the language runtime . How to make this change will be introduced later .

Reference resources :《GO Language practice 》

原网站

版权声明
本文为[Li Jue]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202142013354665.html