Go1.14發布了,快來圍觀新的特性啦

  • 2020 年 3 月 10 日
  • 筆記

如期而至,Go1.14發布了,和往常一樣,該版本保留了Go 1兼容性的承諾,這個版本的大部分更新在工具鏈 、運行時庫的性能提升方面,總的來說,還是在已有的基礎上不斷優化提成,大家期待的泛型還沒有到來,下面一塊看看新的變化吧,以下變化我本地測試過。

Go 1.14 test 優化

go test -v現在將t.Log輸出流式傳輸,而不是在所有測試數據結束時輸出。

testing包的T、B和TB都加上了CleanUp方法,主要作用可以用來測試結束後清理資源,如下程式碼,輸出結果是 test cleanup,clear resourcce , 那麼問題來了,如果我在方法中再加一個defer呢,是Cleanup最後執行還是defer最後執行

func TestCleanup(t *testing.T) {   t.Cleanup(func() {      t.Log("clear resource")   })   t.Log("test cleanup")}

看下面測試程式碼,我們在Cleanup之前和之後都加上defer函數,列印結果如下,我們可以看到,Cleanup還是在defer之後,原理暫時不說了,我也沒研究。

func TestCleanup(t *testing.T) {   defer func() { t.Log("defer resource1") }()   t.Cleanup(func() {      t.Log("clear resource")   })   defer func() { t.Log("defer resource2") }()   t.Log("test cleanup")}test cleanupdefer resource2defer resource1clear resource
Go 1.14 defer優化

defer與直接調用延遲函數相比, 此版本提高了大多數使用的性能,從而產生了幾乎為零的開銷。結果,defer現在可以在對性能至關重要的程式碼中使用,而無需擔心開銷,我們看一下壓測報告

//聲明一個通道type channel chan string//正常關閉func NoDefer() {    ch1 := make(channel, 10)    close(ch1)}//採用defer關閉func Defer() {    ch2 := make(channel, 10)    defer close(ch2)}

Go1.13 的基準測試報告如下

Go1.14 的基準測試報告如下

關於這一改進,官方給出的回應是:Go1.14提高了defer的大多數用法的性能,幾乎0開銷!defer已經可以用於對性能要求很高的場景了.

Go1.14 添加了新包maphash

包maphash提供位元組序列的哈希函數。這些哈希函數用於實現哈希表或其他數據結構,這些數據結構需要將任意字元串或位元組序列映射到無符號64位整數上的統一分布,哈希函數是抗衝突的,但不是加密安全的

func MapHashStudy() {   b := []byte("foo")   h1 := new(maphash.Hash)   h1.Write(b)   //輸出位元組數組的hash值   fmt.Println(h1.Sum64())  //63175979700884496}
Go1.14 time.Timer定時器性能得到「巨幅」提升

下圖是官方的一個壓測數據報告,從基準測試的結果可以看出Go1.14 time包中Ticker等函數性能都得到了「巨幅」提升,數據來源如下,我們可以看到Ticker從 5.4ms 提成到了 0.03ms,非常恐怖的

https://github.com/golang/go/commit/6becb033341602f2df9d7c55cc23e64b925bbee2
Go1.14 goroutine支援非同步搶佔

Go語言調度器的性能隨著版本迭代表現的越來越優異,GMP的概念大家應該都知道,不明白了可以百度一下,這裡不說了。

在Go1.1版本中,調度器還不支援搶佔式調度,只能依靠 goroutine 主動讓出 CPU 資源,存在非常嚴重的調度問題。

Go1.12中編譯器在特定時機插入函數,通過函數調用作為入口觸發搶佔,實現了協作式的搶佔式調度。但是這種需要函數調用主動配合的調度方式存在一些邊緣情況,就比如說下面的例子:

func main() {        runtime.GOMAXPROCS(1)          go func() {                for {                }        }()        time.Sleep(time.Millisecond)        println("OK")}

上面程式碼中,其中創建一個goroutine並掛起, main goroutine 優先調用了 休眠,此時唯一的 P 會轉去執行 for 循環所創建的 goroutine,進而 main goroutine 永遠不會再被調度。換一句話說在Go1.14之前,上邊的程式碼永遠不會輸出OK,因為這種協作式的搶佔式調度是不會使一個沒有主動放棄執行權、且不參與任何函數調用的goroutine被搶佔。

Go1.14 實現了基於訊號的真搶佔式調度解決了上述問題。Go1.14 程式啟動時, 會在函數runtime.sighandler 中註冊了 SIGURG 訊號的處理函數 runtime.doSigPreempt,在觸發垃圾回收的棧掃描時,調用函數掛起goroutine,並向M發送訊號,M收到訊號後,會讓當前goroutine陷入休眠繼續執行其他的goroutine

Go1.14 生態建設

https://pkg.go.dev 是 go.org的配套網站,裡邊有精選用例和其他資源的資訊,提供了godoc.org 之類的 Go 文檔,但它使用起來更方便,並提供了有關軟體包先前版本的資訊,它還可以檢測並顯示許可證,並具有更好的搜索演算法。

如上圖,是網站的首頁,大家可以進去搜索一下,看看有沒有新發現。

最後,Go1.14 還有很多改動
  1. WebAssembly的變化
  2. reflect包的變化
  3. go mod的變化
  4. 很多其他重要的包(math,http等)的改變

很多變化需要大家去探索,本文列出了其中幾個我認為大家必須知道的改變,只是入門,更多原理需要大神們不斷探索,當然我也會儘可能的閱讀源碼研究。

Tags: