- 與java比較
- GMP模型 官方
- GMP模型 來源
- G:Goroutine,实际上我们每次调用 go func 就是生成了一个 G。没有数量限制。但会受内存限制(Goroutine 创建需申请 2-4k 连续内存块)
- P:Processor,处理器,一般 P 的数量就是处理器的核数,可以通过 GOMAXPROCS 进行修改。 本地隊列存放G
- M:Machine,系统线程。默认数量限制是 10000
- 流程:
1. 调用 go func()创建一个goroutine;
2. 新创建的G优先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;
3. M需要在P的本地队列弹出一个可执行的G,如果P的本地队列为空,则先会去全局队列中获取G,如果全局队列也为空则去其他P中偷取G放到自己的P中
4. G将相关参数传输给M,为M执行G做准备
5. 当M执行某一个G时候如果发生了系统调用产生导致M会阻塞,如果当前P队列中有一些G,runtime会将线程M和P分离,然后再获取空闲的线程或创建一个新的内核级的线程来服务于这个P,阻塞调用完成后G被销毁将值返回;
6. 销毁G,将执行结果返回
7. 当M系统调用结束时候,这个M会尝试获取一个空闲的P执行,如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中
- GM模型(舊)與GMP模型(新) 來源
- GM缺點
- 调度 : 获取G全局队列锁,形成了激烈的競争
- 轉移 : P0執行G0生成G1 , 為了繼續執行G0需要轉移給P1執行 , 但G0和G1又共享資料
- GPM:
- 調度 : 因為多了P的本地隊列, 減少鎖競爭
- 轉移 : P0執行G0生成G1 , G1优先存放到P0的本地队列中,如果队列满了,则会把P0队列中一半的G移动到全局队列
- 如果P的本地队列为空,那么他会先到全局队列中获取G,如果全局队列中也没有G,则会尝试从其他线程绑定的P中偷取(steal)一半的G
- 效能調教工具 pyroscope
- wget https://dl.pyroscope.io/release/pyroscope-0.31.0-linux-amd64.tar.gz
- tar -zxvf pyroscope-0.31.0-linux-amd64.tar.gz
- "github.com/pyroscope-io/pyroscope/pkg/agent/profiler"
- profiler.Start(profiler.Config{
- ApplicationName: "name",
- ServerAddress: "address",
- })
- /tmp/pyroscope adhoc go run main.go
- 效能調教工具 pprof
- main.go
- _ "net/http/pprof"
- gorace()
- func gorace() {
- c := make(chan bool)
- m := make(map[string]string)
- go func() {
- m["1"] = "a" // First conflicting access.
- c <- true
- }()
- m["2"] = "b" // Second conflicting access.
- <-c
- for k, v := range m {
- fmt.Println(k, v)
- }
- http.ListenAndServe("localhost:6060", nil)
- }
- 所有效能資訊
- http://localhost:6060/debug/pprof/
- cpu報告
- go tool pprof http://localhost:6060/debug/pprof/profile
- heap報告
- go tool pprof http://localhost:6060/debug/pprof/heap
- cli模式
- top10 前10名
- list [method] 查看method
- pdf 匯出pdf
- flat : func執行所需heap或cpu時間(不包含內部func所需時間)
- cum : func所有所需heap或cpu時間
- online分析報告
- go tool pprof -http=:8080 C:\Users\david\pprof\pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.003.pb.gz