Go1.7裡面的BCE(跳躍檢測排除)(譯文)

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


最近發布的 Go 1.7 裡面使用了一個新的基於 SSA 的編譯後端(目前只有 amd64 可用)。SSA 使得編譯出來的代碼更加高效 SSA,主要是裡面有包含了 BCE和公用子運算式消除。

這篇文章將會通過一些例子給大家展示 Go 1.7 裡面BCE是如何工作的。

在 Go 1.7 裡面我們可以通過這個命令 go build -gcflags="-d=ssa/check_bce/debug=1" 來展示我們的代碼哪一行需要進行越界檢查。

例子1

// example1.gopackage mainfunc f1(s []int) {    _ = s[0] // line 5: bounds check     _ = s[1] // line 6: bounds check     _ = s[2] // line 7: bounds check }func f2(s []int) {    _ = s[2] // line 11: bounds check     _ = s[1] // line 12: bounds check eliminatd!    _ = s[0] // line 13: bounds check eliminatd!}func f3(s []int, index int) {    _ = s[index] // line 17: bounds check     _ = s[index] // line 18: bounds check eliminatd!}func f4(a [5]int) {    _ = a[4] // line 22: bounds check eliminatd!}func main() {}
$ go build -gcflags="-d=ssa/check_bce/debug=1" example1.go# command-line-arguments./11.go:5: Found IsInBounds./11.go:6: Found IsInBounds./11.go:7: Found IsInBounds./11.go:11: Found IsInBounds./11.go:17: Found IsInBounds

我們可以看到這個f2函數裡面的12行和13行不需要進行越界檢查,因為在11行裡面的越界檢查可以保證12,13行的代碼是不會越界的。

但是在f1裡面必須每一行都逐一檢查越界,因為第5行沒辦法保證第6、第7行是安全的,第6行沒辦法保證第7行是安全的。

函數f3裡面,Go 1.7 編譯器知道如果第一行是安全的情況下,那麼第二行的s[index]是完全安全的

Go 1.7 的編輯器也正確的分析出來了f4函數裡面的唯一的一行(22行)也是安全的。

例子2

// example2.gopackage mainfunc f5(s []int) {    for i := range s {        _ = s[i]        _ = s[i:len(s)]        _ = s[:i+1]    }}func f6(s []int) {    for i := 0; i < len(s); i ++ {        _ = s[i]        _ = s[i:len(s)]        _ = s[:i+1]    }}func f7(s []int) {    for i := len(s) - 1; i >= 0; i -- {        _ = s[i] // line 22: bounds check         _ = s[i:len(s)]    }}func f8(s []int, index int) {    if index >= 0 && index < len(s) {        _ = s[index]        _ = s[index:len(s)]    }}func f9(s []int) {    if len(s) > 2 {        _, _, _ = s[0], s[1], s[2]    }}func main() {}
$ go build -gcflags="-d=ssa/check_bce/debug=1" example2.go# command-line-arguments./11.go:22: Found IsInBounds

我們可以看到在例子2裡面只有一行需要進行越界檢查

Go 1.7 編譯器是如此的聰明,它精確的做出決定說:在f5和f6裡面所有的代碼都是安全的

但是好像還是沒有我們人類聰明,看上去22行也是安全的

例子3

// example3.gopackage mainimport "math/rand"func fa() {    s := []int{0, 1, 2, 3, 4, 5, 6}    index := rand.Intn(7)    _ = s[:index] // line 9: bounds check     _ = s[index:] // line 10: bounds check eliminatd!}func fb(s []int, index int) {    _ = s[:index] // line 14: bounds check     _ = s[index:] // line 15: bounds check // not smart enough or a bug?}func fc() {    s := []int{0, 1, 2, 3, 4, 5, 6}    s = s[:4]    index := rand.Intn(7)    _ = s[:index] // line 22: bounds check     _ = s[index:] // line 23: bounds check }func main() {}
$ go build -gcflags="-d=ssa/check_bce/debug=1" example3.go# command-line-arguments./11.go:9: Found IsSliceInBounds./11.go:14: Found IsSliceInBounds./11.go:15: Found IsSliceInBounds./11.go:22: Found IsSliceInBounds./11.go:23: Found IsSliceInBounds

啊!那麼多地方需要進行越界檢查

但是等等,為什麼 Go 1.7 的編譯器會認為第10行是安全的,但是15行和23行不安全?是因為編譯器不夠聰明還是這是一個bug?

實際上,編譯器在這裡是對的!為什嗎?原因是子slice可能大於原始的slice,看下面這個例子:

package mainfunc main() {    s0 := make([]int, 5, 10) // len(s0) == 5, cap(s0) == 10    index := 8    // In golang, for the subslice syntax s[a:b],    // the valid rage for a is [0, len(s)],    // the valid rage for b is [a, cap(s)].    // So, this line is no problem.    _ = s0[:index]    // But, above line is safe can't assure the following line is also safe.    // In fact, it will panic.    _ = s0[index:] // panic: runtime error: slice bounds out of range}

所以如果s[:index]是安全的話,也就當len(s) == cap(s)的時候,s[index:]才是安全的
。這就是為什麼在例子3裡面fb和fc裡面的代碼還需要進行越界檢查。

Go 1.7 編譯器在函數fa裡面成功的檢測到了 len(s) == cap(s)。太棒了!Golang Team!

然而,如果s[index:]是安全的話,那麼s[:index]就一直是安全的。

例子4

// example4.gopackage mainimport "math/rand"func fa2() {    s := []int{0, 1, 2, 3, 4, 5, 6}    index := rand.Intn(7)    _ = s[index:] // line 9: bounds check     _ = s[:index] // line 10: bounds check eliminatd!}func fb2(s []int, index int) {    _ = s[index:] // line 14: bounds check    _ = s[:index] // line 15: bounds check // not smart enough?}func fc2() {    s := []int{0, 1, 2, 3, 4, 5, 6}    s = s[:4]    index := rand.Intn(7)    _ = s[index:] // line 22: bounds check     _ = s[:index] // line 23: bounds check eliminatd!}func main() {}
$ go build -gcflags="-d=ssa/check_bce/debug=1" example4.go# command-line-arguments./11.go:9: Found IsSliceInBounds./11.go:14: Found IsSliceInBounds./11.go:15: Found IsSliceInBounds./11.go:22: Found IsSliceInBounds

在這個例子中, Go 1.7 編譯器成功的計算出來在fc2函數裡面如果22行安全的情況下那麼23行也是安全的,但是在fb2裡面沒辦法計算出來如果14行安全的情況下15行也是安全的情況。

例子5

儘管當前的編譯器(Go1.7.1 amd64) 還是不夠智能的排除一些不需要檢測的地方,但是我們可以做一些暗示來協助編譯器排除這些不必要的越界檢查。

// example5.gopackage mainfunc fd(is []int, bs []byte) {    if len(is) >= 256 {        for _, n := range bs {            _ = is[n] // line 7: bounds check, not smart enough.        }    }}func fd2(is []int, bs []byte) {    if len(is) >= 256 {        is = is[:256] // line 14: bounds check. A hint for the compiler.        for _, n := range bs {            _ = is[n] // line 16: bounds check eliminatd!         }    }}func fe(isa []int, isb []int) {    if len(isa) > 0xFFF {        for _, n := range isb {            _ = isa[n & 0xFFF] // line 24: bounds check, not smart enough.        }    }}func fe2(isa []int, isb []int) {    if len(isa) > 0xFFF {        isa = isa[:0xFFF+1] // line 31: bounds check. A hint for the compiler.        for _, n := range isb {            _ = isa[n & 0xFFF] // line 33: bounds check eliminatd!         }    }}func ff(s []int) []int {    s2 := make([]int, len(s))    for i := range s {        s2[i] = -s[i] // line 41: bounds check, not smart enough.    }    return s2}func ff2(s []int) []int {    s2 := make([]int, len(s))    s2 = s2[:len(s)] // line 48: bounds check. A hint for the compiler.    for i := range s {        s2[i] = -s[i] // line 50: bounds check eliminatd!     }    return s2}func main() {}
$ go build -gcflags="-d=ssa/check_bce/debug=1" example5.go# command-line-arguments./11.go:7: Found IsInBounds./11.go:14: Found IsSliceInBounds./11.go:24: Found IsInBounds./11.go:31: Found IsSliceInBounds./11.go:41: Found IsInBounds./11.go:48: Found IsSliceInBounds

總結

儘管當前1.7版本裡面的 BCE 特性還不夠完美,但是在大多數情況下還是表現的非常好。毫無疑問Go的編譯器將會在後續版本裡面做的更好,感謝 Go 團隊增加了如此幫的特性

參考文獻

  1. gBounds Checking Elimination

  2. Utilizing the Go 1.7 SSA Compiler

原文:http://www.tapirgames.com/blog/golang-1.7-bce


Go社區:https://gocn.io


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.