Go 編譯器是否最佳化了程式碼?
在此程式碼中:
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Second) println(i) }
輸出總是 1然而,令人驚訝的是,1s 足以讓 for 迴圈執行多次。原因是 Go 編譯器正在優化程式碼。
Go 記憶體模型指定了在一個 goroutine 中讀取變數時可以保證觀察到寫入相同變數所產生的值的條件。一個不同的 goroutine。透過增量 i (i = i 1) 對 i 進行賦值後不會發生任何同步事件,因此不能保證任何其他 goroutine 都會觀察到它。事實上,激進的編譯器可能會刪除整個 i 語句。
例如,在這段程式碼中:
package main import "time" func main() { i := 1 go func() { for { i++ } }() <-time.After(1 * time.Millisecond) println(i) }
輸出為 1。 goroutine 簡化為:
"".main.func1 STEXT nosplit size=2 args=0x8 locals=0x0 0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), NOSPLIT, <pre class="brush:php;toolbar:false">for { i++ }
package main import "time" func main() { i := 1 go func() { for { i++ println("+1") } }() <-time.After(1 * time.Millisecond) println(i) }
對於編譯器來說,for 循環可以透過永遠遞增暫存器來實現,本質上是一個無操作的for 迴圈:
+1 +1 << SNIP >> +1 +1 432
插入print 語句後,
"".main.func1 STEXT size=81 args=0x8 locals=0x18 0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), -8 0x0000 00000 (elide.go:7) MOVQ (TLS), CX 0x0009 00009 (elide.go:7) CMPQ SP, 16(CX) 0x000d 00013 (elide.go:7) JLS 74 0x000f 00015 (elide.go:7) SUBQ , SP 0x0013 00019 (elide.go:7) MOVQ BP, 16(SP) 0x0018 00024 (elide.go:7) LEAQ 16(SP), BP 0x001d 00029 (elide.go:7) FUNCDATA <pre class="brush:php;toolbar:false">================== WARNING: DATA RACE Read at 0x00c420094000 by main goroutine: main.main() /home/peter/gopath/src/lucky.go:14 +0xac Previous write at 0x00c420094000 by goroutine 5: main.main.func1() /home/peter/gopath/src/lucky.go:9 +0x4e Goroutine 5 (running) created at: main.main() /home/peter/gopath/src/lucky.go:7 +0x7a ==================
package main import ( "sync" "time" ) func main() { mx := new(sync.Mutex) i := 1 go func() { for { mx.Lock() i++ mx.Unlock() } }() <-time.After(1 * time.Second) mx.Lock() println(i) mx.Unlock() }
41807838
輸出為:
goroutine 擴展為:
goroutine 複雜性的增加意味著編譯器不再考慮將暫存器專用於我。 i 的記憶體值會遞增,這使得主 Goroutine 透過資料競爭使更新可見。
要得到預期結果,請加入一些同步:
輸出:
以上是Go 編譯器是否會最佳化 goroutine 中遞增變數的程式碼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!