Go Wiki: 編譯器與執行時最佳化

此頁面列出了編譯器進行的最佳化。請注意,這些最佳化不一定由語言規範保證。

介面值

介面值中的零寬度型別

將零寬度型別放入介面值中不會進行分配。

  • gc 1.0+
  • gccgo ?

介面值中的字長大小值

將字長大小或更小且非指標的型別放入介面值中不會進行分配。

  • gc: 1.0-1.3,但在 1.4+ 中
  • gccgo: 從未

string[]byte

透過 []byte 進行 map 查詢

對於型別為 map[string]T 的 map m[]byte bm[string(b)] 不會進行分配。(不會建立位元組切片的臨時字串副本)

  • gc 1.4+
  • gccgo ?

[]byte(s) 進行 range

在將 string 轉換為 []byte 以進行位元組範圍遍歷時,不會發生分配

s := "foo"
for i, c := range []byte(s) {
    // ...
}

字串比較轉換

在為了比較目的將 []byte 轉換為 string 時,不會進行分配

var b1 string
var b2 []byte
var x = string(b1) == string(b2) // memeq
var y = string(b1) < string(b2)  // lexicographical comparison
  • gc: 1.5+ (CL 3790)
  • gccgo ?

逃逸分析和內聯

使用 -gcflags -m 觀察 gc 工具鏈的逃逸分析和內聯決策結果。

(待定:解釋 -gcflags -m 的輸出)。

逃逸分析

Gc 編譯器會在函式和包邊界上進行全域性逃逸分析。然而,有很多情況它會放棄。例如,分配給任何型別的間接引用(*p = ...)都會被認為是逃逸的。其他可能阻礙分析的因素包括:函式呼叫、包邊界、切片字面量、子切片和索引等。完整的規則過於複雜,無法在此描述,請檢視 -m 輸出。

  • gc 1.0+
  • gccgo 8.0+.

函式內聯

只有簡短簡單的函式才會被內聯。要被內聯,函式必須符合以下規則:

  • 函式應足夠簡單,AST 節點數量必須小於預算(80);
  • 函式不包含閉包、defer、recover、select 等複雜內容;
  • 函式前面沒有 `go:noinline` 字首;
  • 函式前面沒有 `go:uintptrescapes` 字首,因為逃逸資訊會在內聯過程中丟失;
  • 函式有函式體;
  • 等等。
  • gc 1.0+
  • gccgo: -O1 及以上。

慣用法

最佳化的 memclr

對於切片或陣列 s,以下形式的迴圈:

for i := range s {
    s[i] = <zero value for element of s>
}

會被轉換為高效的執行時 memclr 呼叫。 Issuecommit

  • gc 1.5+
  • gccgo ?

不可掃描物件

當元素型別不包含指標時(對於 map,鍵和值都不包含),垃圾回收器不會掃描切片、通道和 map 的底層緩衝區。這使得可以在記憶體中儲存大量資料集,而不會在垃圾回收時付出高昂代價。例如,以下 map 對 GC 時間的影響不明顯

type Key [64]byte // SHA-512 hash
type Value struct {
    Name      [32]byte
    Balance   uint64
    Timestamp int64
}
m := make(map[Key]Value, 1e8)
  • gc 1.5+
  • gccgo ?

此內容是 Go Wiki 的一部分。