如期而至,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 還有很多改動
- WebAssembly的變化
- reflect包的變化
- go mod的變化
- 很多其他重要的包(math,http等)的改變
很多變化需要大家去探索,本文列出了其中幾個我認爲大家必須知道的改變,只是入門,更多原理需要大神們不斷探索,當然我也會儘可能的閱讀源碼研究。