解讀2016之Golang篇:極速提升,逐步超越

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Go語言已經7歲了!今年8月,Go 1.7如期發布。撰寫本稿時,Go 1.8的測試版也出來了。我們正在熱切盼望著明年2月的Go 1.8正式版。

如果你關注TIOBE的程式設計語言熱門排行榜就會發現,截止到2016年11月,Go語言從原先的第50多位經過多次上竄已經躍到了第13位,躋入絕對主流的程式設計語言的行列!這份熱門排行榜每月都會更新,並基於互連網上的程式員老鳥、教學課程和相關廠商的數量進行排名。在國內,從我這幾年運營Go語言北京使用者組的經曆來看,可以明顯地感覺到Go語言的在國內的大熱。N多初創互連網企業都選用Go語言作為他們的基礎技術棧。我還發現,已經有在大資料、機器人等尖端科技領域耕耘的國內公司開始使用Go語言。這門語言現在已經是無孔不入了。

1. 回顧

遙想去年的1.5版本,Go運行時系統和標準庫剛完成去C化,轉而完全由Go語言和組合語言重寫。到現在,Go的源碼已有了較大的改進,Go語言版本的Go語言也更加成熟了。我下面就帶領大家一起回顧一下Go語言在2016年做出的那些大動作。你可以對比我之前寫的《解讀2015之Golang篇:Golang的全迸發時代》來看。

1.1 極速GC

當然,首先要說的還是效能。Go語言本身最大的效能提升依然在GC(garbage collection,記憶體回收)方面。從Go 1.5時標榜的GC耗時百毫秒級,到今天的全並發GC使得耗時達到毫秒級,再到即將發布的Go 1.8由於實施了諸多改進而達成的百微秒級以下的GC耗時,真可謂是突飛猛進!

圖1 GC停頓時間——Go 1.5 vs. Go 1.6

圖2 GC停頓時間——Go 1.7

在經曆了如此變化之後,如果你現在再說你的Go程式的效能瓶頸在GC上,那隻能讓人側目了。

當然,Go語言對自身效能的提升遠不止於此。

1.2 對HTTP/2的支援

很早以前,Go語言團隊就開始跟進HTTP/2草案了。從Go 1.6開始,我們其實已經可以間接地在Go程式中使用到HTTP/2了,應用情境如:使用Go程式開發基於HTTPS協議的服務端和用戶端。不過,這一切都是自動適配的,Go官方並未暴露出可以指定或配置HTTP/2模組的任何API。另外,在還未發布的Go 1.8中,HTTP/2還會得到更廣泛的支援。

1.3 httptrace包

Go 1.7的標準庫中新增了net/http/httptrace程式碼封裝(https://godoc.org/net/http/httptrace)。它提供了一種調試HTTP請求和響應的方式。你可以像下面這樣輕易地擷取基於HTTP協議的通訊過程的詳細資料。

package mainimport ("context""fmt""log""net/http""net/http/httptrace""os")func main() {traceCtx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{GetConn: func(hostPort string) {fmt.Printf("Prepare to get a connection for %s.\n", hostPort)},GotConn: func(info httptrace.GotConnInfo) {fmt.Printf("Got a connection: reused: %v, from the idle pool: %v.\n",info.Reused, info.WasIdle)},PutIdleConn: func(err error) {if err == nil {fmt.Println("Put a connection to the idle pool: ok.")} else {fmt.Println("Put a connection to the idle pool:", err.Error())}},ConnectStart: func(network, addr string) {fmt.Printf("Dialing... (%s:%s).\n", network, addr)},ConnectDone: func(network, addr string, err error) {if err == nil {fmt.Printf("Dial is done. (%s:%s)\n", network, addr)} else {fmt.Printf("Dial is done with error: %s. (%s:%s)\n", err, network, addr)}},WroteRequest: func(info httptrace.WroteRequestInfo) {if info.Err == nil {fmt.Println("Wrote a request: ok.")} else {fmt.Println("Wrote a request:", info.Err.Error())}},GotFirstResponseByte: func() {fmt.Println("Got the first response byte.")},})req, err := http.NewRequest("GET", "http://www.golang.org/", nil)if err != nil {log.Fatal("Fatal error:", err)}req = req.WithContext(traceCtx)_, err = http.DefaultClient.Do(req)if err != nil {fmt.Fprintf(os.Stderr, "Request error: %v\n", err)os.Exit(1)}}

強烈建議你動手運行一下這個小程式,享受一下掌控全域的感覺。

1.4 子測試

Go 1.7中增加了對子測試(https://blog.golang.org/subtests)的支援,包括功能測試和效能測試。子測試的主要目的是在測試函數中區分和展示因不同的測試參數或測試資料帶來的不同的測試結果。請看下面的測試程式。

package subtestimport ("fmt""math/rand""strconv""testing")// KE 代表鍵-元素對。type KE struct {key     stringelement int}// BenchmarkMapPut 用於對字典的添加和修改操作進行測試。func BenchmarkMapPut(b *testing.B) {max := 5var kes []KEfor i := 0; i <= max; i++ {kes = append(kes, KE{strconv.Itoa(i), rand.Intn(1000000)})}m := make(map[string]int)b.ResetTimer()for _, ke := range kes {k, e := ke.key, ke.elementb.Run(fmt.Sprintf("Key: %s, Element: %#v", k, e), func(b *testing.B) {for i := 0; i < b.N; i++ {m[k] = e + i}})}}

在程式所在目錄下使用go test -run=^$ -bench .命令運行它之後就會看到,針對每一個子測試,go test命令都會列印出一行測試摘要。它們是分離的、獨立統計的。這可以讓我們進行更加精細的測試,細到每次輸入輸出。上述列印內容類別似:

BenchmarkMapPut/Key:_0425,_Element:_498081-4         30000000        40.6 ns/opBenchmarkMapPut/Key:_1540,_Element:_727887-4         30000000        41.7 ns/opBenchmarkMapPut/Key:_2456,_Element:_131847-4         30000000        43.3 ns/opBenchmarkMapPut/Key:_3300,_Element:_984059-4         30000000        46.1 ns/opBenchmarkMapPut/Key:_4694,_Element:_902081-4         30000000        48.4 ns/opBenchmarkMapPut/Key:_5511,_Element:_941318-4         30000000        59.3 ns/opPASSok  _/Users/haolin/infoq-2016_review_go /demo/subtest8.678s

1.5 vendor目錄

在Go 1.5的時候,官方啟用了一個新的環境變數——GO15VENDOREXPERIMENT。該環境變數可以啟動Go的vendor目錄(https://golang.org/s/go15vendor)並用於存放當前程式碼封裝依賴的程式碼封裝。在Go 1.5中,若GO15VENDOREXPERIMENT的值為1則會啟動vendor目錄。Go 1.6正相反,預設支援vendor目錄,當GO15VENDOREXPERIMENT的值為0時禁用vendor目錄。到了Go 1.7,官方完全去掉了這個環境變數。這也代表著對vendor目錄的正式支援。Go語言的實驗特性一般都是按照類似的路數一步步邁向正式版的。

1.6 其他值得一提的改進

1.6.1 檢測並報告對字典的非並發安全訪問

從Go 1.6開始,Go運行時系統對字典的非並發安全訪問採取零容忍的態度。請看下面的程式。

package mainimport "sync"func main() {const workers = 100var wg sync.WaitGroupwg.Add(workers)m := map[int]int{}for i := 1; i <= workers; i++ {go func(i int) {for j := 0; j < i; j++ {m[i]++}wg.Done()}(i)}wg.Wait()}

該程式在未施加任何保護的情況下在多個Goroutine中並發地訪問了字典執行個體m。我們知道,Go原生的字典類型是非並發安全的。所以上面這樣做很可能會讓m的值產生不可預期的變化。這在並發程式中應該堅決避免。在1.6之前,如此操作的Go程式並不會因此崩潰。但是在1.6,運行上述程式後就立刻會得到程式崩潰的結果。Go運行時系統只要檢測到類似代碼,就會強制結束程式並報告錯誤。

1.6.2 sort包的效能提升

Go語言團隊一直致力於標準庫中眾多API的效能提升,並且效果向來顯著。我把sort包單拎出來強調是因為sort.Sort函數因效能最佳化而在行為上稍有調整。在Go 1.6,sort.Sort函數減少了大約10%的比較操作和交換操作的次數,從而獲得了20%~50%的效能提升。不過,這裡有一個副作用,那就是sort.Sort函數的執行會使排序演算法不穩定。所謂不穩定的排序演算法,就是排序可能會使排序因子相等的多個元素在順序上不確定。比如,有如下需要根據長度排序的字串的切片:

var langs= []string{"golang", "erlang", "java", "python", "php", "c++", "perl"}

經sort.Sort函數排序後,該切片只幾個長度相等的元素golang、erlang和python的先後順序可能就不是這樣了,可能會變成erlang、golang、python。雖然它依然會依據排序因子(這裡是字串長度)進行完全正確的排序,但是如此確實可能對一些程式造成影響。

如果你需要穩定的排序,可以使用sort.Stable函數取而代之。

1.6.3 context包進入標準庫

在Go 1.7發布時,標準庫中已經出現了一個名為context的程式碼封裝。該程式碼封裝原先的匯入路徑為golang.org/x/context,而後者現在已經不存在了。context包被正式引入標準庫,並且標準庫中的很多API都因此而做了改變。context.Context類型的值可以協調多個Groutine中的代碼執行“取消”操作,並且可以儲存索引值對。最重要的是它是並發安全的。與它協作的API都可以由外部控制執行“取消”操作,比如:取消一個HTTP請求的執行。

1.6.4 go tool trace的增強

go tool trace自Go 1.5正式加入以來,成為了Go程式調試的又一利器。到了Go 1.7,它已經得到了大幅增強。比如,執行時間的縮短、跟蹤資訊的豐富,等等。

1.6.5 unicode包現基於Unicode 9.0

Go 1.7升級了unicode包,使它支援Unicode 9.0標準。在這之前,它支援的Unicode 8.0標準。

1.6.6 新的編譯器後端——SSA

SSA作為新的編譯器後端,可以讓編譯器產生壓縮比和執行效率都更高的代碼,並為今後的進一步最佳化提供了更有力的支援。在效能方面,它可以讓程式減少5%至35%的CPU使用時間。

到這裡,我向大家展示了Go語言在2016年的一些顯著變化。由於篇幅原因,還有很多Go運行時系統和標準庫的改動沒能提及。尤其是效能方面的改進一直在持續,並潛移默化地為廣大Go程式員提供著底層紅利。

我強烈建議所有Go程式員緊跟Go語言團隊的腳步,升級版本,享受紅利。

2. 展望

2.1 新版本

關於展望,莫過於廣大Go程式員翹首期盼的Go 1.8了。這裡提一下幾個如甘霖般的特性。

  1. Go編寫的HTTP伺服器支援平滑地關閉。這一功能早已由很多第三方程式碼封裝實現,但是這次官方終於給出了答案。
  2. 支援HTTP/2的Server Push。這個就不多說了,肯定會比Hijack更好用。
  3. 新增了plugin包,你可以把一些Go程式作為外掛程式動態地載入進你的程式了。
  4. 更廣泛的上下文支援,自從標準庫中有了context包,它就在很多地方起作用了。很多基於標準庫的介面功能都可以執行“取消”操作。在Go 1.8中,範圍將進一步擴大,比如:database/sql包和testing包都對上下文進行了支援。
  5. sort包的功能改進,對於切片,我們不用再為了使用它的排序功能去編寫某個介面的實作類別型。
  6. go test命令有了新的標記:-mutexprofile。該標記用於提供關於鎖爭用的概要檔案。
  7. 當然,最值得期待的仍然是Go在效能上的提升,尤其是GC方面,又要有一次飛躍了!另外,defer語句的執行會比之前快整整兩倍!cgo的調用開銷也降低了將近一半!

2.2技術社區

其實,除了對Go語言本身的展望,我們也應該憧憬Go社區(尤其是國內Go社區)的發展。中國現在已經差不多是Go程式員最多的國家了。

如果開啟Github上Go語言的Wiki(https://github.com/golang/go/wiki/GoUsers)你就會發現,那裡已經有一個非常長的列表了。其中的公司達到了近200家,相比於2015年年底翻了將近三倍。而且我相信這隻是一小部分,只是在Github上有自己的官方組織且對社區有貢獻的一部分公司。不過,你可能還會發現,在China那一欄下的公司卻只有一家。這是為什麼呢?比較值得深思。我想這也許能從側面反映出對國際技術社區(尤其是開源社區)有貢獻的國內公司太少的問題。在2016年12月初舉辦的一個大型開源技術盛典的講台上,某開源公司CEO提到了程式員對開源社區應該不只索取更要奉獻,這樣才能更好地宣傳和推銷自己。同時,組織機構也不應該成為大家合作的瓶頸。但是,我想國內的實際情況卻恰恰相反。我們國內的電腦技術公司,甚至技術驅動的互連網公司,大都沒有為開源社區做奉獻的習慣,甚至從規章制度上就是明令禁止的。從這方面看,我覺得那個列表中的China欄的慘狀也著實不冤。我熱切盼望到了明年這個China欄能夠變長很多。

不過,從Github以及國內一些代碼託管倉庫上的Go項目數量上看,國人編寫的Go軟體其實已經非常多了。近年來崛起的國內Go開源項目已有不少,特別是(按Star數排列)Gogs、Beego、TiDB、Codis、Pholcus、Hprose、Cyclone等等。他們都已經在國際或國內有了一定的影響力。另外,國人或華人蔘與的國際Go開源項目更是眾多,比如很多人熟知的容器技術領域翹楚Docker、Kubernates、Etcd,等等。

當然,除了一些拔尖的人和拔尖的項目。大多數中國Go程式員和愛好者還是只在國內活躍的。國內的很多地方都自行發起了Go語言使用者組,包括但不限於:北京、上海、深圳、杭州,大連、香港等。在各個地方舉辦的Go語言技術聚會也更加專業、更加頻繁,同時規模更大。僅在北京,2016年參加此類聚會或活動的人次就將近400,Go語言北京使用者組的公眾號(golang-beijing)的粉絲數也超過了2000。據悉,在2017年,各地的Go語言使用者組還會有更大的動作。

我個人認為,如今Go語言的國內推廣已經基本完成了科普階段,現在我們可以實行更加輕鬆的推波助瀾、順水推舟的推廣策略了。由於Go語言的優秀以及不斷的進化,現在自發地關注Go語言的人越來越多了,尤其是在高等學府和編程新手的人群中。

Go語言很好學,配套工具完善,開發和運行效率高,應用領域眾多,國內社區也很活躍,有各種各樣的中文資料和教程,進階並不難,其工程化理念也相當得民心。如果你不是一點時間都沒有的話,我建議你學一學這門簡約、高效的程式設計語言。在互連網時代,尤其是是移動互連網時代,它已經大有作為。即使對於炙手可熱的大資料、微服務等新型領域和理念而言,它也是一個相當重要的技術棧。甚至在即將爆發的人工智慧和機器人大潮,我相信,Go語言也必會大放異彩!

作者介紹

郝林,Go語言北京使用者組的發起人,極客學院Go語言課程顧問,著有圖靈原創圖書《Go並發編程實戰》,同時也是線上免費教程《Go命令教程》和《Go語言第一課》的作者。目前在微賽時代任平台技術負責人。

參考文獻

  1. Go 1.6 Release Notes: https://tip.golang.org/doc/go1.6
  2. Go 1.7 Release Notes: https://tip.golang.org/doc/go1.7
  3. Go 1.8 Release Notes(Beta): https://tip.golang.org/doc/go1.8
  4. The State of Go(2016): https://talks.golang.org/2016/state-of-go.slide
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.