中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

Go 1.9中值得關(guān)注的幾個(gè)變化

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬Linux鏡像隨意使用

Go語言在2016年當(dāng)選tiobe index的年度編程語言。

Go 1.9中值得關(guān)注的幾個(gè)變化

轉(zhuǎn)眼間6個(gè)月過去了,Go在tiobe index排行榜上繼續(xù)強(qiáng)勢(shì)攀升,在最新公布的TIBOE INDEX 7月份的排行榜上,Go挺進(jìn)Top10:

Go 1.9中值得關(guān)注的幾個(gè)變化

還有不到一個(gè)月,Go 1.9版本也要正式Release了(計(jì)劃8月份發(fā)布),當(dāng)前Go 1.9的最新版本是go1.9beta2,本篇的實(shí)驗(yàn)環(huán)境也是基于該版本的,估計(jì)與final go 1.9版本不會(huì)有太大差異了。在今年的GopherChina大會(huì)上,我曾提到:Go已經(jīng)演進(jìn)到1.9,接下來是Go 1.10還是Go 2? 現(xiàn)在答案已經(jīng)揭曉:Go 1.10。估計(jì)Go core team認(rèn)為Go 1還有很多待改善和優(yōu)化的地方,或者說Go2的大改時(shí)機(jī)依舊未到。Go team的tech lead Russ Cox將在今年的GopherCon大會(huì)上做一個(gè)題為”The Future of Go”的主題演講,期待從Russ的口中能夠得到一些關(guān)于Go未來的信息。

言歸正傳,我們還是來看看Go 1.9究竟有哪些值得我們關(guān)注的變化,雖然我個(gè)人覺得Go1.9的變動(dòng)的幅度并不是很大^0^。

一、Type alias

Go 1.9依然屬于Go1系,因此繼續(xù)遵守Go1兼容性承諾。這一點(diǎn)在我的“值得關(guān)注的幾個(gè)變化”系列文章中幾乎每次都要提到。

不過Go 1.9在語言語法層面上新增了一個(gè)“頗具爭(zhēng)議”的語法: Type Alias。關(guān)于type alias的proposal最初由Go語言之父之一的Robert Griesemer提出,并計(jì)劃于Go 1.8加入Go語言。但由于Go 1.8的type alias實(shí)現(xiàn)過于匆忙,測(cè)試不夠充分,在臨近Go 1.8發(fā)布的時(shí)候發(fā)現(xiàn)了無法短時(shí)間解決的問題,因此Go team決定將type alias的實(shí)現(xiàn)從Go 1.8中回退。

Go 1.9 dev cycle伊始,type alias就重新被納入。這次Russ Cox親自撰寫文章《Codebase Refactoring (with help from Go)》為type alias的加入做鋪墊,并開啟新的discussion對(duì)之前Go 1.8的general alias語法形式做進(jìn)一步優(yōu)化,最終1.9僅僅選擇了type alias,而不需要像Go 1.8中g(shù)eneral alias那樣引入新的操作符(=>)。這樣,結(jié)合Go已實(shí)現(xiàn)的interchangeable constant、function、variable,外加type alias,Go終于在語言層面實(shí)現(xiàn)了對(duì)“Gradual code repair(漸進(jìn)式代碼重構(gòu))”理念的初步支持。

注:由于type alias的加入,在做Go 1.9相關(guān)的代碼試驗(yàn)之前,最好先升級(jí)一下你本地編輯器/IDE插件(比如:vim-go、vscode-go)以及各種tools的版本。

官方對(duì)type alias的定義非常簡(jiǎn)單:

An alias declaration binds an identifier to the given type.

我們?cè)趺磥砝斫庑略龅膖ype alias和傳統(tǒng)的type definition的區(qū)別呢?

type T1 T2  // 傳統(tǒng)的type defintion

vs.

type T1 = T2 //新增的type alias

把握住一點(diǎn):傳統(tǒng)的type definition創(chuàng)造了一個(gè)“新類型”,而type alias并沒有創(chuàng)造出“新類型”。如果我們有一個(gè)名為“孫悟空”的類型,那么我們可以寫出如下有意思的代碼:

type  超級(jí)賽亞人  孫悟空
type  卡卡羅特 = 孫悟空

這時(shí),我們擁有了兩個(gè)類型:孫悟空超級(jí)賽亞人。我們以孫悟空這個(gè)類型為藍(lán)本定義一個(gè)超級(jí)賽亞人類型;而當(dāng)我們用到卡卡羅特這個(gè)alias時(shí),實(shí)際用的就是孫悟空這個(gè)類型,因?yàn)榭ǹ_特就是孫悟空,孫悟空就是卡卡羅特。

我們用幾個(gè)小例子再來仔細(xì)對(duì)比一下:

1、賦值

Go強(qiáng)調(diào)“顯式類型轉(zhuǎn)換”,因此采用傳統(tǒng)type definition定義的新類型在其變量被賦值時(shí)需對(duì)右側(cè)變量進(jìn)行顯式轉(zhuǎn)型,否則編譯器就會(huì)報(bào)錯(cuò)。

//github.com/bigwhite/experiments/go19-examples/typealias/typedefinitions-assignment.go
package main

// type definitions
type MyInt int
type MyInt1 MyInt

func main() {
    var i int = 5
    var mi MyInt = 6
    var mi1 MyInt1 = 7

    mi = MyInt(i)  // ok
    mi1 = MyInt1(i) // ok
    mi1 = MyInt1(mi) // ok

    mi = i   //Error: cannot use i (type int) as type MyInt in assignment
    mi1 = i  //Error: cannot use i (type int) as type MyInt1 in assignment
    mi1 = mi //Error: cannot use mi (type MyInt) as type MyInt1 in assignment
}

而type alias并未創(chuàng)造新類型,只是源類型的“別名”,在類型信息上與源類型一致,因此可以直接賦值:

//github.com/bigwhite/experiments/go19-examples/typealias/typealias-assignment.go
package main

import "fmt"

// type alias
type MyInt = int
type MyInt1 = MyInt

func main() {
    var i int = 5
    var mi MyInt = 6
    var mi1 MyInt1 = 7

    mi = i // ok
    mi1 = i // ok
    mi1 = mi // ok

    fmt.Println(i, mi, mi1)
}

2、類型方法

Go1中通過type definition定義的新類型,新類型不會(huì)“繼承”源類型的method set:

// github.com/bigwhite/experiments/go19-examples/typealias/typedefinition-method.go
package main

// type definitions
type MyInt int
type MyInt1 MyInt

func (i *MyInt) Increase(a int) {
    *i = *i + MyInt(a)
}

func main() {
    var mi MyInt = 6
    var mi1 MyInt1 = 7
    mi.Increase(5)
    mi1.Increase(5) // Error: mi1.Increase undefined (type MyInt1 has no field or method Increase)
}

但是通過type alias方式得到的類型別名卻擁有著源類型的method set(因?yàn)楸揪褪且粋(gè)類型),并且通過alias type定義的method也會(huì)反映到源類型當(dāng)中:

// github.com/bigwhite/experiments/go19-examples/typealias/typealias-method1.go
package main

type Foo struct{}
type Bar = Foo

func (f *Foo) Method1() {
}

func (b *Bar) Method2() {
}

func main() {
    var b Bar
    b.Method1() // ok

    var f Foo
    f.Method2() // ok
}

同樣對(duì)于源類型為非本地類型的,我們也無法通過type alias為其增加新method:

//github.com/bigwhite/experiments/go19-examples/typealias/typealias-method.go
package main

type MyInt = int

func (i *MyInt) Increase(a int) { // Error: cannot define new methods on non-local type int
    *i = *i + MyInt(a)
}

func main() {
    var mi MyInt = 6
    mi.Increase(5)
}

3、類型embedding

有了上面關(guān)于類型方法的結(jié)果,其實(shí)我們也可以直接知道在類型embedding中type definition和type alias的差異。

// github.com/bigwhite/experiments/go19-examples/typealias/typedefinition-embedding.go
package main

type Foo struct{}
type Bar Foo

type SuperFoo struct {
    Bar
}

func (f *Foo) Method1() {
}

func main() {
    var s SuperFoo
    s.Method1() //Error: s.Method1 undefined (type SuperFoo has no field or method Method1)
}

vs.

// github.com/bigwhite/experiments/go19-examples/typealias/typealias-embedding.go

package main

type Foo struct{}
type Bar = Foo

type SuperFoo struct {
    Bar
}

func (f *Foo) Method1() {
}

func main() {
    var s SuperFoo
    s.Method1() // ok
}

通過type alias得到的alias Bar在被嵌入到其他類型中,其依然攜帶著源類型Foo的method set。

4、接口類型

接口類型的identical的定義決定了無論采用哪種方法,下面的賦值都成立:

// github.com/bigwhite/experiments/go19-examples/typealias/typealias-interface.go
package main

type MyInterface interface{
    Foo()
}

type MyInterface1 MyInterface
type MyInterface2 = MyInterface

type MyInt int

func (i *MyInt)Foo() {

}

func main() {
    var i MyInterface = new(MyInt)
    var i1 MyInterface1 = i // ok
    var i2 MyInterface2 = i1 // ok

    print(i, i1, i2)
}

5、exported type alias

前面說過type alias和源類型幾乎是一樣的,type alias有一個(gè)特性:可以通過聲明exported type alias將package內(nèi)的unexported type導(dǎo)出:

//github.com/bigwhite/experiments/go19-examples/typealias/typealias-export.go
package main

import (
    "fmt"

    "github.com/bigwhite/experiments/go19-examples/typealias/mylib"
)

func main() {
    f := &mylib.Foo{5, "Hello"}
    f.String()            // ok
    fmt.Println(f.A, f.B) // ok

    // Error:  f.anotherMethod undefined (cannot refer to unexported field
    // or method mylib.(*foo).anotherMethod)
    f.anotherMethod()
}

而mylib包的代碼如下:

package mylib

import "fmt"

type foo struct {
    A int
    B string
}

type Foo = foo

func (f *foo) String() {
    fmt.Println(f.A, f.B)
}

func (f *foo) anotherMethod() {
}

二、Parallel Complication(并行編譯)

Go 1.8版本的gc compiler的編譯性能雖然照比Go 1.5剛自舉時(shí)已經(jīng)提升了一大截兒,但依然有提升的空間,雖然Go team沒有再像Go 1.6時(shí)對(duì)改進(jìn)compiler性能那么關(guān)注。

在Go 1.9中,在原先的支持包級(jí)別的并行編譯的基礎(chǔ)上又實(shí)現(xiàn)了包函數(shù)級(jí)別的并行編譯,以更為充分地利用多核資源。默認(rèn)情況下并行編譯是enabled,可以通過GO19CONCURRENTCOMPILATION=0關(guān)閉。

在aliyun ECS一個(gè)4核的vm上,我們對(duì)比了一下并行編譯和關(guān)閉并行的差別:

# time GO19CONCURRENTCOMPILATION=0 go1.9beta2 build -a std

real    0m16.762s
user    0m28.856s
sys    0m4.960s

# time go1.9beta2 build -a std

real    0m13.335s
user    0m29.272s
sys    0m4.812s

可以看到開啟并行編譯后,gc的編譯性能約提升20%(realtime)。

在我的Mac 兩核pc上的對(duì)比結(jié)果如下:

$time GO19CONCURRENTCOMPILATION=0 go build -a std

real    0m16.631s
user    0m36.401s
sys    0m8.607s

$time  go build -a std

real    0m14.445s
user    0m36.366s
sys    0m7.601s

提升大約13%。

三、”./…”不再匹配vendor目錄

自從Go 1.5引入vendor機(jī)制以來,Go的包依賴問題有所改善,但在vendor機(jī)制的細(xì)節(jié)方面依然有很多提供的空間。

比如:我們?cè)趃o test ./…時(shí),我們期望僅執(zhí)行我們自己代碼的test,但Go 1.9之前的版本會(huì)匹配repo下的vendor目錄,并將vendor目錄下的所有包的test全部執(zhí)行一遍,以下面的repo結(jié)構(gòu)為例:

$tree vendor-matching/
vendor-matching/
├── foo.go
├── foo_test.go
└── vendor
    └── mylib
        ├── mylib.go
        └── mylib_test.go

如果我們使用go 1.8版本,則go test ./…輸出如下:

$go test ./...
ok      github.com/bigwhite/experiments/go19-examples/vendor-matching    0.008s
ok      github.com/bigwhite/experiments/go19-examples/vendor-matching/vendor/mylib    0.009s

我們看到,go test將vendor下的包的test一并執(zhí)行了。關(guān)于這點(diǎn),gophers們?cè)趃o repo上提了很多issue,但go team最初并沒有理會(huì)這個(gè)問題,只是告知用下面的解決方法:

$go test $(go list ./... | grep -v /vendor/)

不過在社區(qū)的強(qiáng)烈要求下,Go team終于妥協(xié)了,并承諾在Go 1.9中fix該issue。這樣在Go 1.9中,你會(huì)看到如下結(jié)果:

$go test ./...
ok      github.com/bigwhite/experiments/go19-examples/vendor-matching    0.008s

這種不再匹配vendor目錄的行為不僅僅局限于go test,而是適用于所有官方的go tools。

四、GC性能

GC在Go 1.9中依舊繼續(xù)優(yōu)化和改善,大多數(shù)程序使用1.9編譯后都能得到一定程度的性能提升。1.9 release note中尤其提到了大內(nèi)存對(duì)象分配性能的顯著提升。

在”go runtime metrics“搭建一文中曾經(jīng)對(duì)比過幾個(gè)版本的GC,從我的這個(gè)個(gè)例的圖中來看,Go 1.9與Go 1.8在GC延遲方面的指標(biāo)性能相差不大:

Go 1.9中值得關(guān)注的幾個(gè)變化

五、其他

下面是Go 1.9的一些零零碎碎的改進(jìn),這里也挑我個(gè)人感興趣的說說。

1、Go 1.9的新安裝方式

go 1.9的安裝增加了一種新方式,至少beta版支持,即通過go get&download安裝:

# go get golang.org/x/build/version/go1.9beta2

# which go1.9beta2
/root/.bin/go18/bin/go1.9beta2
# go1.9beta2 version
go1.9beta2: not downloaded. Run 'go1.9beta2 download' to install to /root/sdk/go1.9beta2

# go1.9beta2 download
Downloaded 0.0% (15208 / 94833343 bytes) ...
Downloaded 4.6% (4356956 / 94833343 bytes) ...
Downloaded 34.7% (32897884 / 94833343 bytes) ...
Downloaded 62.6% (59407196 / 94833343 bytes) ...
Downloaded 84.6% (80182108 / 94833343 bytes) ...
Downloaded 100.0% (94833343 / 94833343 bytes)
Unpacking /root/sdk/go1.9beta2/go1.9beta2.linux-amd64.tar.gz ...
Success. You may now run 'go1.9beta2'

# go1.9beta2 version
go version go1.9beta2 linux/amd64

# go1.9beta2 env GOROOT
/root/sdk/go1.9beta2

go1.9 env輸出支持json格式:

# go1.9beta2 env -json
{
    "CC": "gcc",
    "CGO_CFLAGS": "-g -O2",
    "CGO_CPPFLAGS": "",
    "CGO_CXXFLAGS": "-g -O2",
    "CGO_ENABLED": "1",
    "CGO_FFLAGS": "-g -O2",
    "CGO_LDFLAGS": "-g -O2",
    "CXX": "g++",
    "GCCGO": "gccgo",
    "GOARCH": "amd64",
    "GOBIN": "/root/.bin/go18/bin",
    "GOEXE": "",
    "GOGCCFLAGS": "-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build750457963=/tmp/go-build -gno-record-gcc-switches",
    "GOHOSTARCH": "amd64",
    "GOHOSTOS": "linux",
    "GOOS": "linux",
    "GOPATH": "/root/go",
    "GORACE": "",
    "GOROOT": "/root/sdk/go1.9beta2",
    "GOTOOLDIR": "/root/sdk/go1.9beta2/pkg/tool/linux_amd64",
    "PKG_CONFIG": "pkg-config"
}

2、go doc支持查看struct field的doc了

我們使用Go 1.8查看net/http包中struct Response的某個(gè)字段Status:

# go doc net/http.Response.Status
doc: no method Response.Status in package net/http
exit status 1

Go 1.8的go doc會(huì)報(bào)錯(cuò)! 我們?cè)賮砜纯碐o 1.9:

# go1.9beta2 doc net/http.Response.Status
struct Response {
    Status string  // e.g. "200 OK"
}

# go1.9beta2 doc net/http.Request.Method
struct Request {
    // Method specifies the HTTP method (GET, POST, PUT, etc.).
    // For client requests an empty string means GET.
    Method string
}

3、核心庫的變化

a) 增加monotonic clock支持

在2017年new year之夜,歐美知名CDN服務(wù)商Cloudflare的DNS出現(xiàn)大規(guī)模故障,導(dǎo)致歐美很多網(wǎng)站無法正常被訪問。之后,Cloudflare工程師分析了問題原因,罪魁禍?zhǔn)拙驮谟趃olang time.Now().Sub對(duì)時(shí)間的度量?jī)H使用了wall clock,而沒有使用monotonic clock,導(dǎo)致返回負(fù)值。而引發(fā)異常的事件則是新年夜際授時(shí)組織在全時(shí)間范圍內(nèi)添加的那個(gè)閏秒(leap second)。一般來說,wall clock僅用來告知時(shí)間,mnontonic clock才是用來度量時(shí)間流逝的。為了從根本上解決問題,Go 1.9在time包中實(shí)現(xiàn)了用monotonic clock來度量time流逝,這以后不會(huì)出現(xiàn)時(shí)間的“負(fù)流逝”問題了。這個(gè)改動(dòng)不會(huì)影響到gopher對(duì)timer包的方法層面上的使用。

b) 增加math/bits包

在一些算法編程中,經(jīng)常涉及到對(duì)bit位的操作。Go 1.9提供了高性能math/bits package應(yīng)對(duì)這個(gè)問題。關(guān)于bits操作以及算法,可以看看經(jīng)典著作《Hacker’s Delight》。這里就不舉例了。

c) 提供了一個(gè)支持并發(fā)的Map類型

Go原生的map不是goroutine-safe的,盡管在之前的版本中陸續(xù)加入了對(duì)map并發(fā)的檢測(cè)和提醒,但gopher一旦需要并發(fā)map時(shí),還需要自行去實(shí)現(xiàn)。在Go 1.9中,標(biāo)準(zhǔn)庫提供了一個(gè)支持并發(fā)的Map類型:sync.Map。sync.Map的用法比較簡(jiǎn)單,這里簡(jiǎn)單對(duì)比一下builtin map和sync.Map在并發(fā)環(huán)境下的性能:

我們自定義一個(gè)簡(jiǎn)陋的支持并發(fā)的類型:MyMap,來與sync.Map做對(duì)比:

// github.com/bigwhite/experiments/go19-examples/benchmark-for-map/map_benchmark.go
package mapbench

import "sync"

type MyMap struct {
    sync.Mutex
    m map[int]int
}

var myMap *MyMap
var syncMap *sync.Map

func init() {
    myMap = &MyMap{
        m: make(map[int]int, 100),
    }

    syncMap = &sync.Map{}
}

func builtinMapStore(k, v int) {
    myMap.Lock()
    defer myMap.Unlock()
    myMap.m[k] = v
}

func builtinMapLookup(k int) int {
    myMap.Lock()
    defer myMap.Unlock()
    if v, ok := myMap.m[k]; !ok {
        return -1
    } else {
        return v
    }
}

func builtinMapDelete(k int) {
    myMap.Lock()
    defer myMap.Unlock()
    if _, ok := myMap.m[k]; !ok {
        return
    } else {
        delete(myMap.m, k)
    }
}

func syncMapStore(k, v int) {
    syncMap.Store(k, v)
}

func syncMapLookup(k int) int {
    v, ok := syncMap.Load(k)
    if !ok {
        return -1
    }

    return v.(int)
}

func syncMapDelete(k int) {
    syncMap.Delete(k)
}

針對(duì)上面代碼,我們寫一些并發(fā)的benchmark test,用偽隨機(jī)數(shù)作為key:

// github.com/bigwhite/experiments/go19-examples/benchmark-for-map/map_benchmark_test.go
package mapbench

import "testing"

func BenchmarkBuiltinMapStoreParalell(b *testing.B) {
    b.RunParallel(func(pb *testing.PB) {
        r := rand.New(rand.NewSource(time.Now().Unix()))
        for pb.Next() {
            // The loop body is executed b.N times total across all goroutines.
            k := r.Intn(100000000)
            builtinMapStore(k, k)
        }
    })
}

func BenchmarkSyncMapStoreParalell(b *testing.B) {
    b.RunParallel(func(pb *testing.PB) {
        r := rand.New(rand.NewSource(time.Now().Unix()))
        for pb.Next() {
            // The loop body is executed b.N times total across all goroutines.
            k := r.Intn(100000000)
            syncMapStore(k, k)
        }
    })
}
... ...

我們執(zhí)行一下benchmark:

$go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/bigwhite/experiments/go19-examples/benchmark-for-map
BenchmarkBuiltinMapStoreParalell-4         3000000           515 ns/op
BenchmarkSyncMapStoreParalell-4            2000000           754 ns/op
BenchmarkBuiltinMapLookupParalell-4        5000000           396 ns/op
BenchmarkSyncMapLookupParalell-4          20000000            60.5 ns/op
BenchmarkBuiltinMapDeleteParalell-4        5000000           392 ns/op
BenchmarkSyncMapDeleteParalell-4          30000000            59.9 ns/op
PASS
ok      github.com/bigwhite/experiments/go19-examples/benchmark-for-map    20.550s

可以看出,除了store,lookup和delete兩個(gè)操作,sync.Map都比我自定義的粗糙的MyMap要快好多倍,似乎sync.Map對(duì)read做了特殊的優(yōu)化(粗略看了一下代碼:在map read這塊,sync.Map使用了無鎖機(jī)制,這應(yīng)該就是快的原因了)。

d) 支持profiler labels

通用的profiler有時(shí)并不能完全滿足需求,我們時(shí)常需要沿著“業(yè)務(wù)相關(guān)”的執(zhí)行路徑去Profile。Go 1.9在runtime/pprof包、go tool pprof工具增加了對(duì)label的支持。Go team成員rakyll有一篇文章“Profiler labels in go”詳細(xì)介紹了profiler labels的用法,可以參考,這里不贅述了。

六、后記

正在寫這篇文章之際,Russ Cox已經(jīng)在GopherCon 2017大會(huì)上做了”The Future of Go”的演講,并announce Go2大幕的開啟,雖然只是號(hào)召全世界的gopher們一起help and plan go2的設(shè)計(jì)和開發(fā)。同時(shí),該演講的文字版已經(jīng)在Go官網(wǎng)發(fā)布了,文章名為《Toward Go 2》,顯然這又是Go語言演化史上的一個(gè)里程碑的時(shí)刻,值得每個(gè)gopher為之慶賀。不過Go2這枚靴子真正落地還需要一段時(shí)間,甚至很長(zhǎng)時(shí)間。當(dāng)下,我們還是要繼續(xù)使用和改善Go1,就讓我們從Go 1.9開始吧^0^。

本文涉及的demo代碼可以在這里下載。

來自:Go 1.9中值得關(guān)注的幾個(gè)變化

標(biāo)簽: dns linux 代碼 服務(wù)商 轉(zhuǎn)型

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:Nginx 通過 Lua + Redis 實(shí)現(xiàn)動(dòng)態(tài)封禁 IP

下一篇:Java中處理異常的9個(gè)最佳實(shí)踐