1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一起看看 Go 1.20 新特性有哪些?

        共 12602字,需瀏覽 26分鐘

         ·

        2022-11-22 10:04

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

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

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

        1. 語(yǔ)法變化

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

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

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

        package main

        import "fmt"

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

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

        在Go 1.20版本之前如果要實(shí)現(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ù)組的指針,通過(guò)sl或parr對(duì)底層數(shù)組元素的修改都能在對(duì)方身上體現(xiàn)出來(lái)。但是arr則是底層數(shù)組的一個(gè)副本,后續(xù)通過(guò)sl對(duì)切片的修改或通過(guò)parr對(duì)底層數(shù)組的修改都不會(huì)影響arr,反之亦然。

        不過(guò)這種trick語(yǔ)法還不是那么直觀!于是上面那個(gè)“允許將切片直接轉(zhuǎn)換為數(shù)組”的issue便提了出來(lái)。我們?cè)?span style="color: rgb(30, 107, 184);font-weight: bold;">go playground[8]上選擇“go dev branch”便可以使用最新go tip的代碼,我們嘗試一下最新語(yǔ)法:

        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不再報(bào)錯(cuò),但其語(yǔ)義與前面的“var arr = ([7]int)(sl)”語(yǔ)義是相同的,即返回一個(gè)切片底層數(shù)組的副本,arr不會(huì)受到后續(xù)切片元素變化的影響。

        不過(guò)這里也有個(gè)約束,那就是轉(zhuǎn)換后的數(shù)組長(zhǎng)度要小于等于切片長(zhǎng)度,否則會(huì)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

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

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

        1) profile-guided optimization

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

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

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

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

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

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

        為什么Go發(fā)行版Size越來(lái)越大呢?這很大程度是因?yàn)镚o發(fā)行版中包含了GOROOT下所有軟件包的預(yù)編譯.a文件,以go 1.19的macos版本為例,在$GOROOT/pkg下,我們看到下面這些.a文件,用du查看一下占用的磁盤(pán)空間,達(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 .

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

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

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

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

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

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

        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這樣的代碼會(huì)被放置到每條分支路徑下。

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

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

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

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

        4) 廢棄-i flag

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

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

        1) 支持wrap multiple errors

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

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

        interface {
            Unwrap() []error
        }

        func Join(errs ...error) error

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

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

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

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

        我們看一下下面這個(gè)例子:

        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編譯運(yùn)行上面程序:

        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編譯上面程序的運(yùn)行結(jié)果為:

        e = error1 error2, error3, error4
        true
        true
        true

        將fmt.Errorf一行換為:

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

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

        e = error1
        error2
        error3
        error4
        true
        true
        true

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

        2) 新增arena實(shí)驗(yàn)包

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

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

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

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

        3) time包變化

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

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

        time包還為T(mén)ime增加了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時(shí)間格式是使用最廣泛的時(shí)間格式,其解析性能在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è)置自動(dòng)設(shè)置相關(guān)build tags[22]
        • 標(biāo)準(zhǔn)庫(kù)增加cyypto/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泛型語(yǔ)法落地: 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è)計(jì)文檔: https://github.com/golang/proposal/blob/master/design/55022-pgo-implementation.md

        [13] 

        Go團(tuán)隊(duì)決定從Go 1.20開(kāi)始發(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] 

        這個(gè)是一個(gè)早計(jì)劃好的“廢棄動(dòng)作”: https://github.com/golang/go/issues/41696

        [16] 

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

        [17] 

        arena這個(gè)proposal: https://github.com/golang/go/issues/51317

        [18] 

        time包增加了三個(gè)時(shí)間layout格式常量: https://github.com/golang/go/issues/52746

        [19] 

        time包還為T(mén)ime增加了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è)置[自動(dòng)設(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

        [27] 

        “Gopher部落”知識(shí)星球: https://wx.zsxq.com/dweb2/index/group/51284458844544



        推薦閱讀


        福利

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

        瀏覽 288
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            校花穿超短裙让我c到高潮 | 美女露出让男人桶爽 | 扒开双腿精油私密按摩电视剧 | 男人添女人下部高潮全视频 | 免费观看日皮视频 | 狠狠2018 | 美女露100%胸无遮挡免费观看 | 小h片 | 美少妇3p | 欧美AAAAAAA |