曹大带我学 Go(10)—— 如何给 Go 提性能优化的 pr
你好,我是小X。
曹大最近開 Go 課程了,小X 正在和曹大學 Go。
這個系列會講一些從課程中學到的讓人醍醐灌頂的東西,撥云見日,帶你重新認識 Go。
之前 qcrao 寫了一篇《成為 Go Contributor》 的文章,講了如何給 Go 提一個 typo 的 pr,以此熟悉整個流程。當然,離真正的 Contributor 還差得遠。
開課前曹大在 Go 夜讀上講了他給 Go 提的一個關于 tls 的性能優化,課上又細講了下,本文就帶大家來學習下他優化了啥以及如何看優化效果。
第一次提的 pr 在這里,之后又挪到了一個新的位置,前后有一些代碼上的簡化,最后看著挺舒服。
優化前每個 tls 連接上都有一個 write buffer,但是活躍的連接數很少,很多內存都被閑置了,這種就可以用 sync.Pool 來優化了。
conn用 sync.Pool 緩存 []byte,并順帶將連接上的一個 outBuf 字段給干掉了:
files changed整體上改動挺少,效果也不錯。
雖然一開始給了 _test 文件,但其實并不能太好反映性能的提升。因此后面曹大又寫了一個簡單的 client 和 server 來實際測試。
我在開發機上測了一下,優化還是挺明顯的。這又是一個使用 pprof 查看性能優化的好例子。
client 的代碼如下:
package?mainimport?("crypto/tls""fmt""io/ioutil""net/http""os""strconv""sync""go.uber.org/ratelimit" )func?main()?{url?:=?os.Args[3]connNum,?err?:=?strconv.ParseInt(os.Args[1],?10,?64)if?err?!=?nil?{fmt.Println(err)return}qps,?err?:=?strconv.ParseInt(os.Args[2],?10,?64)if?err?!=?nil?{fmt.Println(err)return}bucket?:=?ratelimit.New(int(qps))var?l?sync.MutexconnList?:=?make([]*http.Client,?connNum)for?i?:=?0;?;?i++?{bucket.Take()i?:=?igo?func()?{l.Lock()if?connList[i%len(connList)]?==?nil?{connList[i%len(connList)]?=?&http.Client{Transport:?&http.Transport{TLSClientConfig:?????&tls.Config{InsecureSkipVerify:?true},IdleConnTimeout:?????0,MaxIdleConns:????????1,MaxIdleConnsPerHost:?1,},}}conn?:=?connList[i%len(connList)]l.Unlock()if?resp,?e?:=?conn.Get(url);?e?!=?nil?{fmt.Println(e)}?else?{defer?resp.Body.Close()ioutil.ReadAll(resp.Body)}}()}}邏輯比較簡單,就是固定連接數、固定 QPS 向服務端發請求。
server 的代碼如下:
package?mainimport?("fmt""net/http"_?"net/http/pprof" )var?content?=?make([]byte,?16000)func?sayhello(wr?http.ResponseWriter,?r?*http.Request)?{wr.Header()["Content-Length"]?=?[]string{fmt.Sprint(len(content))}wr.Header()["Content-Type"]?=?[]string{"application/json"}wr.Write(content) }func?main()?{go?func()?{http.ListenAndServe(":3333",?nil)}()http.HandleFunc("/",?sayhello)err?:=?http.ListenAndServeTLS(":4443",?"server.crt",?"server.key",?nil)if?err?!=?nil?{fmt.Println(err)} }邏輯也很簡單,起了一個 tls server,并注冊了一個 sayhello 接口。
啟動 server 后,先用 1.15(1.17 之前的版本都可以,曹大的改動還沒合入)測試:
go?run?server.go#?1000?個連接,100?個?QPS go?run?client.go?1000?100?https://localhost:4443查看 server 的內存 profile。后面還會用 --base 的命令,比較前后兩個 profile 文件的差異。
pprof 的命令如下:
go?tool?pprof?--http=:8000?http://127.0.0.1:3333/debug/pprof/heap Go 1.15 mem profile看看這個大“平頂山”,有那味了(平頂山表示可以優化,如果是那種特別窄的尖尖就沒辦法了)~
因為這個 pr 已經合到了 1.17,我們再用 1.17 來測一下:
go1.17rc1?run?server.go go1.17rc1?run?client.go?1000?100?https://localhost:4443 Go 1.17 mem profile為了使用 --base 命令來進行比較,需要把 profile 文件保存下來:
curl?http://127.0.0.1:3333/debug/pprof/heap?>?mem.1.14 curl?http://127.0.0.1:3333/debug/pprof/heap?>?mem.1.17最后來比較優化前后的差異:
go?tool?pprof?-http=:8000?--base?mem.1.15?mem.1.17 --base優化效果還是很明顯的。我們來看菜單欄里的 view->top:
view->top整個優化從最終的提交來看還挺簡單,但是能發現問題所在,并能結合自己的知識儲備進行優化還是挺難的。我們平時也要多積累相關的優化經驗,到關鍵時候才能頂上去。像 pprof 的使用,要自己多加練習。
好了,這就是今天全部的內容了~ 我是小X,我們下期再見~
參考資料
[1]
tls 的性能優化:?https://www.bilibili.com/video/BV1Z64y1m7uc
[2]這里:?https://go-review.googlesource.com/c/go/+/263277
[3]位置:?https://go-review.googlesource.com/c/go/+/267957
歡迎關注曹大的 TechPaper 以及碼農桃花源~
總結
以上是生活随笔為你收集整理的曹大带我学 Go(10)—— 如何给 Go 提性能优化的 pr的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决 GraphQL 的限流难题
- 下一篇: 从容器到容器云,什么才是 Kuberne