Go1.1新特性介紹(語言和庫更完善/性能提高約30%)
前幾天GCC4.8發布, 已經部分包含Go1.1特性, 詳細介紹:
根據golang-nuts的消息, 4月第1周可能會進入Go1.1發布流程(就是下周). 要修復的問題還剩20多一點的, 估計應該不會出現大的延期.
補充: Go1.1正式版本已經于2013.05.14正式發布.
補充: 目前還標記為Go1.1的剩余BUG主要是gccgo相關的, gccgo1.1的發布流程和GCC4.8.1同步.
Go1.1主要的目標是性能的優化和一些bug的修復, 詳細內容參考:
關于Go的性能測試數據(性能和C語言gcc -O2性能基本沒有差異):
補充: BenchmarksGame的測試不同語言實現差別較大, 比如: Go的binary-trees開了很多goroutine, C的程序開了omp優化等.
Go1.1的更新主要涉及 語言/實現/性能優化/標準庫幾個部分.
補充: Go1.1的二進制安裝包將包含gotour程序(啟動命令: go tool tour).
一、語言的改變
Go1發布時曾作出承諾, 保證在Go1.x發布后不會修改之前的語言特性. 這里有一些問題的修復, 還有一些新增加的特性.
整數除以零是編譯錯誤
在Go1中, 整數被一個常量0除會導致一個運行時 panic:
- func f(x int) int {
- return x/0
- }
在 Go1.1 中, 整數被一個常量0將會被當作一個編譯錯誤處理.
Unicode代理區碼點不能用于面值
字符串和 rune 字面值的定義更加嚴格. Unicode代理區碼點不能用于面值. 細節請參考后面的 Unicode 章節.
方法值和方法表達式
Go1.1新實現了方法值(method values), 它是綁定到receiver值的一個閉包. 比如有一個實現了Writer 的 w 值, 那么 w.Write 將等價于下面的閉包函數:
- func (p []byte) (n int, err error) {
- return w.Write(p)
- }
方法值(method values)不同于方法表達式(method expressions), 方法表達式是從一個類型對應的函數. 比如 (*bufio.Writer).Write 和下面的普通函數類型:
- func (w *bufio.Writer, p []byte) (n int, err error) {
- return w.Write(p)
- }
更新: 現有的代碼不需要更新, 這個是新加的特性.
GoSpec中給出了很多例子:
- f := t.Mv; f(7) // like t.Mv(7)
- f := pt.Mp; f(7) // like pt.Mp(7)
- f := pt.Mv; f(7) // like (*pt).Mv(7)
- f := t.Mp; f(7) // like (&t).Mp(7)
- f := makeT().Mp // invalid: result of makeT() is not addressable
有了方法值, Go1.1可以從interface值中取出方法值(Go1.0不支持方法值):
- var i interface { M(int) } = myVal
- f := i.M; f(7) // like i.M(7)
這樣改動的好處是類型的方法和interface方法完全統一了.
Return requirements
在Go1.1之前, 函數如果有返回值的話, 則最后必須有一個retune或panic語句.
- func abs(x int) int {
- if x >= 0 {
- return x
- } else {
- return -x
- }
- }
會有以下編譯錯誤:
function ends without a return statement
之前一般可以在末尾加一個panic來回避這個問題:
- func abs(x int) int {
- if x >= 0 {
- return x
- } else {
- return -x
- }
- panic("not reachable")
- }
在Go1.1規范, 對函數的終結語句做了定義:
主要有以下幾種類型:
- return或者goto語句
- 調用內置的panic函數
- if語句: 必須帶else, 并且if和else部分都有明確的終結語句
- for語句: 死循環的類型(無退出條件和break語句)
- switch語句: 沒有break語句, 必須有default分支, 每個分支都有終結語句(或者是fallthrough到下個分支的終結語句)
- select語句: 無break語句, 必須有default分支, 每個分支都有終結語句
- 用于goto的Label
已有的代碼可以不用更新, 當然有些代碼可以寫的更簡化.
#p#
二、實現和工具的變化
gccgo的變化
上個月發布的 GCC 4.8.0 還沒有完整的包含 Go1.1. 確實的主要功能是沒有方法值, 標準庫也有一些差異. 可以期望5月份發布GCC4.8.1時, gccgo能夠完整支持Go1.1.
命令行參數解析
在目前的gc工具鏈中, 編譯器和連接器使用的是同樣的命令行參數解析規則, 基于Go語言的flag包實現. 和傳統的UNIX命令行習慣有些不同. 這可能影響直接調用GC工具的腳本. 例如, 原有的 go tool 6c -Fw -Dfoo 命令, 現在要這樣寫 go tool 6c -F -w -D foo.
64位系統 int 大小為int64
語言規范運行實現自由選擇 int 和 uint 為32位或64位. 在之前的實現中, int 和 uint都是32位. 現在, 在 AMD64/x86-64 平臺, GC和gccgo實現的int 和 uint 都是64位的. 一個相關的變化是, 在64位系統切片將可以分配超出int32能表示的20多億個元素.
更新: 大部分代碼不受影響. 如果可能會影響涉及 int 類型轉換有關的代碼:
- x := ^uint32(0) // x is 0xffffffff
- i := int(x) // i is -1 on 32-bit systems, 0xffffffff on 64-bit
- fmt.Println(i)
下面是一種可移植的寫法(-1在所有系統是可以確定的):
- i := int(int32(x))
64位平臺的堆大小
對于64位平臺, 堆的最大上限擴大很大, 從幾個GB到幾十個GB(具體細節取決于系統,并且可能會更改).
在32位系統, 堆的大小沒有變化.
更新: 現有代碼沒有影響. 當時新程序可以使用更多的內存.
補充: Windows/amd64目前默認為32GB(以后會根據不同版本調整).
Unicode
主要是和UTF16相關的代理區碼點有關:
- 代理區碼點不能用在字符/字符串面值中.
- 代理區碼點的輸出也有變化
比如:
- import "fmt"
- func main() {
- fmt.Printf("%+q\n", string(0xD800))
- }
Go 1.0輸出為 "\ud800", Go 1.1 輸出為 "\ufffd".
Race detector
go tool內置數據競爭檢測工具. 目前只支持64位系統. 使用時需要指定-race選項.
比如以下的代碼, 在2個不同goroutine中競爭訪問m.
- func main() {
- c := make(chan bool)
- m := make(map[string]string)
- go func() {
- m["1"] = "a" // First conflicting access.
- c <- true
- }()
- m["2"] = "b" // Second conflicting access.
- <-c
- for k, v := range m {
- fmt.Println(k, v)
- }
- }
可以這樣測試:
- $ go run -race mysrc.go // to run the source file
補充: 檢測工具目前是基于LLVM的ThreadSanitizer race detector實現的.
gc assemblers
主要是為了適應64位系統int的默認大小變化, 和其他一些內部約定的變化.
go 的變化
go get時必須設置GOPATH, 并且GOPATH和GOROOT不能相同.
補充: 建議兲朝用戶手工下載, 因為go get默認使用的https協議經常被墻.
go test 的變化
當啟動了剖析選項時, go test默認不在刪除二進制測試程序. 有專門的選項-cpuprofile:
- $ go test -cpuprofile cpuprof.out mypackage
還有-blockprofile選項, 可以檢測goroutines被阻塞情況.
更多細節請參考: go help test
go fix 的變化
現在go fix將不再支持Go1之前的代碼到Go1的轉換. 如果需要處理Go1之前的代碼, 需要先使用Go1的工具做預處理.
新的構建約束
如果只在Go1.1+環境編譯, 可以設置以下構建選項:
- // +build go1.1
如果是Go1.0.x的變化條件, 則是:
- // +build !go1.1
新支持的平臺
Go1.1工具鏈實驗性的增加freebsd/arm, netbsd/386, netbsd/amd64, netbsd/arm, openbsd/386 和 openbsd/amd64平臺的支持.
對于 freebsd/arm 或 netbsd/arm 必須是ARMv6或更高的版本.
Go1.1對于linux/arm平臺實驗性的提供cgo的支持.
交叉編譯
交叉編譯時, 默認禁止CGO. 如果需要啟動CGO, 需要手工設置CGO_ENABLED=1.
三、性能優化
主要有以下幾個地方:
- gc編譯器生成代碼優化, 特別是Intel 32-bit下的浮點運算
- gc編譯器采用更多的內聯優化, 比如內置的append函數和interface的轉換等
- map的一個改進實現, 顯著減少內存碎片和CPU時間
- 在多核的CPU上, 可以并行的運行垃圾回收
- 更精確的垃圾回收, 可以顯著減少堆的大小, 特別是在32位系統
- 運行時和網絡庫配合更緊密, 減少上下文切換代價
- 標準庫的優化
根據官方的說法, Go1.1性能提升基本有30%-40%, 有時更多(當然也有不明顯的情況).
補充: Windows版本很多優化的代碼還沒有合并進來, 特別是運行時/網絡部分.
四、標準庫的變化
reflect包功能完善: 實現了select的支持; 類型轉換支持; 變量到閉包的轉換;chan/map/slice的支持等.- 新加的包:
go/format/net/http/cookiejar/runtime/race - 其他很多包的問題修復/功能完善/性能優化 等.
這個部分細節太多, 具體查看官方文檔吧.


























