<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 1.20 快訊:新特性有哪些?一文了解

          共 11600字,需瀏覽 24分鐘

           ·

          2022-12-09 19:11

          在近期Russ Cox代表Go核心團(tuán)隊發(fā)表的“Go, 13周年”[1]一文中,他提到了“在Go的第14個年頭,Go團(tuán)隊將繼續(xù)努力使Go成為用于大規(guī)模軟件工程的最好的環(huán)境,將特別關(guān)注供應(yīng)鏈安全,提高兼容性和結(jié)構(gòu)化日志記錄,當(dāng)然還會有很多其他改進(jìn),包括profile-guided optimization等”。

          當(dāng)前正在開發(fā)的版本是Go 1.20,預(yù)計2023年2月正式發(fā)布,這個版本也將是Go在其第14個年頭發(fā)布的第一個版本。很多人沒想到Go真的會進(jìn)入到Go 1.2x版本,而不是Go 2.x。記得Russ Cox曾說過可能永遠(yuǎn)也不會有Go2了,畢竟Go泛型語法落地[2]這么大的語法改動也沒有讓Go1兼容性承諾[3]失效。

          目前Go 1.20版本正在如火如荼的開發(fā)中,很多gopher都好奇Go 1.20版本會帶來哪些新特性?在這篇文章中,我就帶大家一起去Go 1.20 milestone[4]的issues列表中翻翻,提前看看究竟會有哪些新特性加入Go。

          1. 語法變化

          Go在其1.18版本[5]迎來了自開源以來最大規(guī)模的語法變化,然后呢?就沒有然后了。Go在語法演進(jìn)上再次陷入沉寂,沒錯,這就是Go長期以來堅持的風(fēng)格。

          如果Go 1.20版本真有語法層面的變化,那估計就是這個issue了:“spec: allow conversion from slice to array”[6],即允許切片類型到數(shù)組類型的類型轉(zhuǎn)換

          在Go 1.20版本之前,我們以Go 1.19版本[7]為例寫下下面代碼:

                
                package?main

          import?"fmt"

          func?main()?{
          ????var?sl?=?[]int{1,?2,?3,?4,?5,?6,?7}
          ??? var arr =?[7]int(sl)?//?編譯器報錯:cannot convert sl (variable of type?[]int)?to?type?[7]int
          ????fmt.Println(sl)
          ????fmt.Println(arr)
          }

          這段代碼中,我們進(jìn)行了一個[]int到[7]int的類型轉(zhuǎn)換,但在Go 1.19版本編譯器針對這個轉(zhuǎn)換會報錯!即不支持將切片類型顯式轉(zhuǎn)換數(shù)組類型。

          在Go 1.20版本之前如果要實現(xiàn)切片到數(shù)組的轉(zhuǎn)換,是有trick的,看下面代碼:

                
                func?main()?{
          ????var?sl?=?[]int{1,?2,?3,?4,?5,?6,?7}
          ????var?parr?=?(*[7]int)(sl)
          ????var?arr?=?*(*[7]int)(sl)
          ????fmt.Println(sl)??//?[1?2?3?4?5?6?7]
          ????fmt.Println(arr)?//?[1?2?3?4?5?6?7]
          ????sl[0]?=?11
          ????fmt.Println(sl)????//?[11?2?3?4?5?6?7]
          ????fmt.Println(arr)???//?[1?2?3?4?5?6?7]
          ????fmt.Println(*parr)?//?[11?2?3?4?5?6?7]
          }

          該trick的理論基礎(chǔ)是Go允許獲取切片的底層數(shù)組地址。在上面的例子中parr就是指向切片sl底層數(shù)組的指針,通過sl或parr對底層數(shù)組元素的修改都能在對方身上體現(xiàn)出來。但是arr則是底層數(shù)組的一個副本,后續(xù)通過sl對切片的修改或通過parr對底層數(shù)組的修改都不會影響arr,反之亦然。

          不過這種trick語法還不是那么直觀!于是上面那個“允許將切片直接轉(zhuǎn)換為數(shù)組”的issue便提了出來。我們在go playground[8]上選擇“go dev branch”便可以使用最新go tip的代碼,我們嘗試一下最新語法:

                
                func?main()?{
          ?var?sl?=?[]int{1,?2,?3,?4,?5,?6,?7}
          ?var?arr?=?[7]int(sl)??????
          ?var?parr?=?(*[7]int)(sl)
          ?fmt.Println(sl)???//?[1?2?3?4?5?6?7]
          ?fmt.Println(arr)??//?[1?2?3?4?5?6?7]
          ?sl[0]?=?11
          ?fmt.Println(arr)??//?[1?2?3?4?5?6?7]
          ?fmt.Println(parr)?//?&[11?2?3?4?5?6?7]
          }

          我們看到直接將sl轉(zhuǎn)換為數(shù)組arr不再報錯,但其語義與前面的“var arr =?([7]int)(sl)”語義是相同的,即返回一個切片底層數(shù)組的副本,arr不會受到后續(xù)切片元素變化的影響。

          不過這里也有個約束,那就是轉(zhuǎn)換后的數(shù)組長度要小于等于切片長度,否則會panic:

                
                var?sl?=?[]int{1,?2,?3,?4,?5,?6,?7}
          var?arr?=?[8]int(sl)?//?panic:?runtime?error:?cannot?convert?slice?with?length?7?to?array?or?pointer?to?array?with?length?8

          在寫本文時,該issue尚未close,不過進(jìn)入最終Go 1.20版本應(yīng)該不是大問題。

          2. 編譯器/鏈接器和其他工具鏈

          1) profile-guided optimization

          Go編譯器團(tuán)隊一直致力于對Go編譯器/鏈接器的優(yōu)化,這次在Go 1.20版本中,該團(tuán)隊很大可能會給我們帶來“profile-guided optimization”[9]。

          什么是“profile-guided optimization”呢?原先Go編譯器實施的優(yōu)化手段,比如內(nèi)聯(lián)[10],都是基于固定規(guī)則決策的,所有信息都來自編譯的Go源碼。而這次的“profile-guided optimization”[11]顧名思義,需要源碼之外的信息做“制導(dǎo)”來決定實施哪些優(yōu)化,這個源碼之外的信息就是profile信息,即來自pprof工具在程序運行時采集的數(shù)據(jù),如下圖(圖來自profile-guided optimization設(shè)計文檔[12])所示:

          6c4aedf30be8dbd8a3d686bc75450eac.webp

          因此pgo優(yōu)化實際上是需要程序員參與的,程序員拿著程序到生產(chǎn)環(huán)境跑,程序生成的profile性能采集數(shù)據(jù)會被保存下來,然后這些profile采集數(shù)據(jù)會提供給Go編譯器,以在下次構(gòu)建同一個程序時輔助優(yōu)化決策。由于這些profile是來自生產(chǎn)環(huán)境或模擬生產(chǎn)環(huán)境的數(shù)據(jù),使得這種優(yōu)化更有針對性。并且,Google數(shù)據(jù)中心其他語言(C/C++)實施PGO優(yōu)化的效果顯示,優(yōu)化后的性能保守估計提升幅度在5%~15%。

          和其他新引入的特性一樣,Go 1.20將包含該特性,但默認(rèn)并不開啟,我們可以手動開啟進(jìn)行體驗,未來版本,pgo特性才會默認(rèn)為auto開啟。

          2) 大幅減小Go發(fā)行版包的Size

          隨著Go語言的演進(jìn),Go發(fā)行版的Size也在不斷增加,從最初的幾十M到如今的上百M。本地電腦里多安裝幾個Go版本,(解壓后)幾個G就沒有了,此外Size大也讓下載時間變得更長,尤其是一些網(wǎng)絡(luò)環(huán)境不好的地區(qū)。

          為什么Go發(fā)行版Size越來越大呢?這很大程度是因為Go發(fā)行版中包含了GOROOT下所有軟件包的預(yù)編譯.a文件,以go 1.19的macos版本為例,在$GOROOT/pkg下,我們看到下面這些.a文件,用du查看一下占用的磁盤空間,達(dá)111M:

                
                $ls
          archive/?database/?fmt.a??index/??mime/??plugin.a?strconv.a?time/
          bufio.a??debug/??go/??internal/?mime.a??reflect/?strings.a?time.a
          bytes.a??embed.a??hash/??io/??net/??reflect.a?sync/??unicode/
          compress/?encoding/?hash.a??io.a??net.a??regexp/??sync.a??unicode.a
          container/?encoding.a?html/??log/??os/??regexp.a?syscall.a?vendor/
          context.a?errors.a?html.a??log.a??os.a??runtime/?testing/
          crypto/??expvar.a?image/??math/??path/??runtime.a?testing.a
          crypto.a?flag.a??image.a??math.a??path.a??sort.a??text/

          $du?-sh
          111M?.

          而整個pkg目錄的size為341M,占Go 1.19版本總大小495M的近70%。

          于是在Go社區(qū)提議下,Go團(tuán)隊決定從Go 1.20開始發(fā)行版不再為GOROOT中的大多數(shù)軟件包提供預(yù)編譯的.a文件[13],新版本將只包括GOROOT中使用cgo的幾個軟件包的.a文件。

          因此Go 1.20版本中,GOROOT下的源碼將像其他用戶包那樣在構(gòu)建后被緩存到本機(jī)cache中。此外,go install也不會為GOROOT軟件包安裝.a文件,除非是那些使用cgo的軟件包。這樣Go發(fā)行版的size將最多減少三分之二。

          取而代之的是,這些包將在需要時被構(gòu)建并緩存在構(gòu)建緩存中,就像已經(jīng)為GOROOT之外的非主包所做的那樣。此外,go install也不會為GOROOT軟件包安裝.a文件,除非是那些使用cgo的軟件包。這些改變是為了減少Go發(fā)行版的大小,在某些情況下可以減少三分之二。

          3) 擴(kuò)展代碼覆蓋率(coverage)報告到應(yīng)用本身

          想必大家都用過go test的輸出過代碼覆蓋率,go test會在unit test代碼中注入代碼以統(tǒng)計unit test覆蓋的被測試包路徑,下面是代碼注入的舉例:

                
                func?ABC(x?int)?{
          ????if?x?<?0?{
          ????????bar()
          ????}
          }

          注入代碼后:

                
                func?ABC(x?int)?{GoCover_0_343662613637653164643337.Count[9]?=?1;
          ??if?x?<?0?{GoCover_0_343662613637653164643337.Count[10]?=?1;
          ????bar()
          ??}
          }

          像GoCover_xxx這樣的代碼會被放置到每條分支路徑下。

          不過go test -cover也有一個問題,那就是它只是適合針對包收集數(shù)據(jù)并提供報告,它無法針對應(yīng)用整體給出代碼覆蓋度報告。

          Go 1.20版本中有關(guān)的“extend code coverage testing to include applications”[14]的proposal就是來擴(kuò)展代碼覆蓋率的,可以支持對應(yīng)用整體的覆蓋率統(tǒng)計和報告。

          該特性在Go 1.20版本中也將作為實驗性特性,默認(rèn)是off的。該方案通過go build -cover方式生成注入了覆蓋率統(tǒng)計代碼的應(yīng)用程序,在應(yīng)用執(zhí)行過程中,報告會被生成到指定目錄下,我們依然可以通過go tool cover來查看這個整體性報告。

          此外,新proposal在實現(xiàn)原理上與go test -cover差不多,都是source-to-source的方案,這樣后續(xù)也可以統(tǒng)一維護(hù)。當(dāng)然Go編譯器也會有一些改動。

          4) 廢棄-i flag

          這個是一個早計劃好的“廢棄動作”[15]。自從Go 1.10引入go build cache后,go build/install/test -i就不會再將編譯好的包安裝到$GOPATH/pkg下面了。

          3. Go標(biāo)準(zhǔn)庫

          1) 支持wrap multiple errors

          Go 1.20增加了一種將多個error包裝(wrap)為一個error的機(jī)制[16],方便從打包后的錯誤的Error方法中一次性得到包含一系列關(guān)于該錯誤的相關(guān)錯誤的信息。

          這個機(jī)制增加了一個(匿名)接口和一個函數(shù):

                
                interface?{
          ????Unwrap()?[]error
          }

          func?Join(errs?...error)?error

          同時增強(qiáng)了像fmt.Errorf這樣的函數(shù)的語義,當(dāng)在Errorf中使用多個%w verb時,比如:

                
                e?:=?errors.Errorf("%w,?%w,?%w",?e1,?e2,?e3)

          Errorf將返回一個將e1, e2, e3打包完的且實現(xiàn)了上述帶有Unwrap() []error方法的接口的錯誤類型實例。

          Join函數(shù)的語義是將傳入的所有err打包成一個錯誤類型實例,該實例同樣實現(xiàn)了上述帶有Unwrap() []error方法的接口,且該錯誤實例的類型的Error方法會返回?fù)Q行符間隔的錯誤列表。

          我們看一下下面這個例子:

                
                package?main

          import?(
          ?"errors"
          ?"fmt"
          )

          type?MyError?struct?{
          ?s?string
          }

          func?(e?*MyError)?Error()?string?{
          ?return?e.s
          }

          func?main()?{
          ?e1?:=?errors.New("error1")
          ?e2?:=?errors.New("error2")
          ?e3?:=?errors.New("error3")
          ?e4?:=?&MyError{
          ??s:?"error4",
          ?}
          ?e?:=?fmt.Errorf("%w,?%w,?%w,?%w",?e1,?e2,?e3,?e4)

          ?fmt.Printf("e?=?%s\n",?e.Error())?//?error1?error2,?error3,?error4
          ?fmt.Println(errors.Is(e,?e1))?//?true

          ?var?ne?*MyError
          ?fmt.Println(errors.As(e,?&ne))?//?true
          ?fmt.Println(ne?==?e4)?//?true
          }

          我們首先在Go 1.19編譯運行上面程序:

                
                e?=?error1?%!w(*errors.errorString=&{error2}),?%!w(*errors.errorString=&{error3}),?%!w(*main.MyError=&{error4})
          false
          false
          false

          顯然Go 1.19的fmt.Errorf函數(shù)尚不支持多%w verb。

          而Go 1.20編譯上面程序的運行結(jié)果為:

                
                e?=?error1?error2,?error3,?error4
          true
          true
          true

          將fmt.Errorf一行換為:

                
                e?:=?errors.Join(e1,?e2,?e3,?e4)?

          再運行一次的結(jié)果為:

                
                e?=?error1
          error2
          error3
          error4
          true
          true
          true

          即Join函數(shù)打包后的錯誤類型實例類型的Error方法會返回?fù)Q行符間隔的錯誤列表。

          2) 新增arena實驗包

          Go是帶GC語言,雖然Go GC近幾年持續(xù)改進(jìn),絕大多數(shù)場合都不是大問題了。但是在一些性能敏感的領(lǐng)域,GC過程占用的可觀算力還是讓應(yīng)用吃不消。

          降GC消耗,主要思路就是減少堆內(nèi)存分配、減少反復(fù)的分配與釋放。Go社區(qū)的某些項目為了減少內(nèi)存GC壓力,在mmaped內(nèi)存上又建立一套GC無法感知到的簡單內(nèi)存管理機(jī)制并在適當(dāng)場合應(yīng)用。但這些自實現(xiàn)的、脫離GC的內(nèi)存管理都有各自的問題。

          Go 1.18版本發(fā)布前,arena這個proposal[17]就被提上了日程,arena包又是google內(nèi)部的一個實驗包,據(jù)說效果還不錯的(在改進(jìn)grpc的protobuf反序列化實驗上),可以節(jié)省15%的cpu和內(nèi)存消耗。但proposal一出,便收到了來自各方的comment,該proposal在Go 1.18和Go 1.19一度處于hold狀態(tài),直到Go 1.20才納入到試驗特性,我們可以通過GOEXPERIMENT=arena開啟該機(jī)制。

          arena包主要思路其實是“整體分配,零碎使用,再整體釋放”,以最大程度減少對GC的壓力。關(guān)于arena包,等進(jìn)一步完善后,后續(xù)可能會有專門文章分析。

          3) time包變化

          time包增加了三個時間layout格式常量[18],相信不用解釋,大家也知道如何使用:

                
                ?DateTime???=?"2006-01-02?15:04:05"
          ?DateOnly???=?"2006-01-02"
          ?TimeOnly???=?"15:04:05"

          time包還為Time增加了Compare方法[19],適用于time之間的>=和<=比較:

                
                //?Compare?returns?-1?if?t1?is?before?t2,?0?if?t1?equals?t2?or?1?if?t1?is?after?t2.
          func?(t1?Time)?Compare(t2?Time)?int

          此外,time包的RFC3339時間格式是使用最廣泛的時間格式,其解析性能在Go 1.20中得到優(yōu)化,提升了70%左右,格式化性能提升30%[20]。

          4. 其他

          • Go 1.17版本將作為Go 1.20的bootstrap編譯器;
          • Go編譯器性能提升3%[21];
          • Go工具鏈將根據(jù)GO[arch]環(huán)境變量的設(shè)置自動設(shè)置相關(guān)build tags[22];
          • 標(biāo)準(zhǔn)庫增加crypto/ecdh包[23],提供安全的、基于byte切片的ECDH API;
          • bytes, strings包增加Clone函數(shù)[24];
          • strings包增加CutPrefix和CutSuffix函數(shù)[25];
          • text/template的解析性能提升40%[26]。

          5. 參考資料

          • Go 1.20 milestone - https://github.com/golang/go/milestone/250
          • Exploring Go's Profile-Guided Optimizations - https://www.polarsignals.com/blog/posts/2022/09/exploring-go-profile-guided-optimizations/
          • What's coming to go 1.20 - https://twitter.com/mvdan_/status/1588242469577117696


          參考資料

          [1]?

          “Go, 13周年”:?https://tonybai.com/2022/11/11/go-opensource-13-years

          [2]?

          Go泛型語法落地:?https://tonybai.com/2022/04/20/some-changes-in-go-1-18

          [3]?

          Go1兼容性承諾:?https://go.dev/doc/go1compat

          [4]?

          Go 1.20 milestone:?https://github.com/golang/go/milestone/250

          [5]?

          1.18版本:?https://tonybai.com/2022/04/20/some-changes-in-go-1-18

          [6]?

          “spec: allow conversion from slice to array”:?https://github.com/golang/go/issues/46505

          [7]?

          Go 1.19版本:?https://tonybai.com/2022/08/22/some-changes-in-go-1-19

          [8]?

          go playground:?https://go.dev/play

          [9]?

          “profile-guided optimization”:?https://github.com/golang/proposal/blob/master/design/55022-pgo.md

          [10]?

          內(nèi)聯(lián):?https://tonybai.com/2022/10/17/understand-go-inlining-optimisations-by-example

          [11]?

          “profile-guided optimization”:?https://github.com/golang/go/issues/55022

          [12]?

          profile-guided optimization設(shè)計文檔:?https://github.com/golang/proposal/blob/master/design/55022-pgo-implementation.md

          [13]?

          Go團(tuán)隊決定從Go 1.20開始發(fā)行版不再為GOROOT中的大多數(shù)軟件包提供預(yù)編譯的.a文件:?https://github.com/golang/go/issues/47257

          [14]?

          “extend code coverage testing to include applications”:?https://github.com/golang/proposal/blob/master/design/51430-revamp-code-coverage.md

          [15]?

          這個是一個早計劃好的“廢棄動作”:?https://github.com/golang/go/issues/41696

          [16]?

          增加了一種將多個error包裝(wrap)為一個error的機(jī)制:?https://github.com/golang/go/issues/53435#issuecomment-1191752789

          [17]?

          arena這個proposal:?https://github.com/golang/go/issues/51317

          [18]?

          time包增加了三個時間layout格式常量:?https://github.com/golang/go/issues/52746

          [19]?

          time包還為Time增加了Compare方法:?https://github.com/golang/go/issues/50770

          [20]?

          其解析性能在Go 1.20中得到優(yōu)化,提升了70%左右,格式化性能提升30%:?https://github.com/golang/go/issues/50770

          [21]?

          Go編譯器性能提升3%:?https://go-review.googlesource.com/c/go/+/432897

          [22]?

          arch]環(huán)境變量的設(shè)置[自動設(shè)置相關(guān)build tags:?https://github.com/golang/go/issues/45454

          [23]?

          增加cyypto/ecdh包:?https://github.com/golang/go/issues/52221

          [24]?

          bytes, strings包增加Clone函數(shù):?https://github.com/golang/go/issues/45038

          [25]?

          strings包增加CutPrefix和CutSuffix函數(shù):?https://github.com/golang/go/issues/42537

          [26]?

          text/template的解析性能提升40%:?https://github.com/golang/go/issues/53261




          推薦閱讀


          福利
          我為大家整理了一份 從入門到進(jìn)階的Go學(xué)習(xí)資料禮包 ,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。 關(guān)注公眾號 「polarisxu」,回復(fù)? ebook ?獲?。贿€可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉伊人综合 | 在线青青草网站 | 黑逼逼无码区 | 亚洲一卡二卡 | 爆操美女视频 |