Go 性能優化技巧 2/10

對於一些初學者,自知道 Go 裏面的 array 以 pass-by-value 方式傳遞後,就莫名地引發 「恐慌」。外加諸多文章未做說明,就建議用 slice 代替 array,企圖避免數據拷貝,提高性能。實際上,此作法有待商榷。某些時候怕會拔苗助長,倒形成沒必要要的性能損失。函數

用個簡單的示例說明。性能

package main

import (
    "fmt"
)

const capacity = 1024

func array() [capacity]int {
    var d [capacity]int

    for i := 0; i < len(d); i++ {
        d[i] = 1
    }

    return d
}

func slice() []int {
    d := make([]int, capacity)

    for i := 0; i < len(d); i++ {
        d[i] = 1
    }

    return d
}

func main() {
    fmt.Println(array())
    fmt.Println(slice())
}

代碼很簡單,兩個函數分別返回 「內容相同」 的 array 和 slice。爲避免編譯器優化,特填充了所有數據,以模擬 「真實」 數據複製行爲。接下來,看看性能測試對比。測試

package main

import (
    "testing"
)

func BenchmarkArray(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = array()
    }
}

func BenchmarkSlice(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = slice()
    }

}

圖片描述

這結果怕是顛覆了最初認知。array 非但擁有更好的性能,還避免了堆內存分配,也就是說減輕了 GC 壓力。爲何會這樣?優化

熟悉彙編的,怕是很容易看出來。函數 array 返回值的複製只需用 "CX + REP" 指令就可完成。spa

圖片描述

整個 array 函數徹底在棧上完成,而 slice 函數則需執行 makeslice,繼而在堆上分配內存,這就是問題所在。code

圖片描述

對於一些短小的對象,複製成本遠小於在堆上分配和回收操做。對象

Go Proverbs: A little copying is better than a little dependency.圖片


最新動態,請掃碼關注
圖片描述內存

相關文章
相關標籤/搜索