1. 程式人生 > >Go1.14釋出了,快來圍觀新的特性啦

Go1.14釋出了,快來圍觀新的特性啦

如期而至,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 cleanup
defer resource2
defer resource1
clear 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等)的改變

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