線上Golang程式 GC調優一例

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

Golang 是一個很有意思的語言,第一次看它介紹時,就很喜歡。半年前加入美團,有機會用它寫了幾個線上程式。其中一個程式Router,每天需要轉寄幾千萬的請求。由於需要根據請求內容決定route路徑,它需要載入幾十萬deal(美團單)的資訊到記憶體供查詢。問題來了,用map裝的幾十萬資料讓gc很辛苦。

Deal資料

// Deal的定義type DealTiny struct {Dealid    int32Classid   int32Mttypeid  int32Bizacctid int32Isonline  boolGeocnt    int32}

gc停頓

用go寫一個簡單的Web程式,設定GOGCTRACE環境變數為1後啟動程式,用wrk壓力測試,觀察控制台打出的gc停頓時間。

GOGCTRACE=1 go run gc.go  # 設定環境變數,go gc時會列印詳細資料wrk http://localhost:8080/ -d 10s  # 壓力測試,發送大量請求,讓程式“忙”起來,觸發gc

測試程式主要部分code:

func main() {const SIZE = 500000 // 50萬m := make(map[int32]DealTiny, SIZE)for i := 0; i < SIZE; i++ { // 把資料放進記憶體m[rand.Int31()] = DealTiny{}}http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {// 類比記憶體配置,做一些計算n := rand.Intn(4096) + 1024buffer := make([]int, n)for i := 0; i < n; i++ {buffer[i] = rand.Intn(1024)}c := 0for i := 0; i < n; i++ {if buffer[i] > 512 {c += 1}}fmt.Fprintf(w, "n: %d, more than 512 count: %d", n, c)})log.Fatal(http.ListenAndServe(":8080", nil))}

程式在控制台的部分輸出

# go 1.1.1; Linux 3.2.0; CPU Intel(R) Core(TM) i7-2600 CPU 3.40GHzgc83(1): 8+0+0 ms, 62 -> 31 MB 19455 -> 3211 (1291202-1287991) objects, 0(0) handoff, 0(0) steal, 0/0/0 yieldsgc84(1): 8+0+0 ms, 62 -> 31 MB 19087 -> 3213 (1307079-1303866) objects, 0(0) handoff, 0(0) steal, 0/0/0 yieldsgc85(1): 8+0+0 ms, 62 -> 31 MB 18935 -> 3212 (1322802-1319590) objects, 0(0) handoff, 0(0) steal, 0/0/0 yields

gc停頓時間為8ms,且線上CPU比測試機主頻低,且是虛擬機器,停頓時間比8ms長一些。這麼長的停頓時間,顯然是不能接受的。需要想辦法最佳化。

查看go的代碼 src/pkg/runtime/mgc0.c#985發現,gc時,需要一個一個的掃描map的key和value,自然是相當貴的。

go沒有像jvm那樣多的可以調整的參數,並且不是分代回收。最佳化gc的方式僅僅只能是通過最佳化程式。但go有一個優勢:有真正的array(而僅僅是an array of referece)。go的gc演算法是mark and sweep,array對此是友好的:整個array一次性被處理。可以用一個array用open addressing的方式實現map,以此最佳化gc(也會減少記憶體的使用,後面可以看到)

// DealMap 為array backend hash tabledm := NewDealMap(SIZE)for i := 0; i < SIZE; i++ {    dm.Put(DealTiny{Dealid: rand.Int31()})}

此次,gc日誌為

gc80(1): 0+0+0 ms, 25 -> 12 MB 7235 -> 803 (507340-506537) objects, 0(0) handoff, 0(0) steal, 0/0/0 yieldsgc81(1): 0+0+0 ms, 25 -> 12 MB 7184 -> 803 (513722-512919) objects, 0(0) handoff, 0(0) steal, 0/0/0 yieldsgc82(1): 0+0+0 ms, 25 -> 12 MB 7340 -> 803 (520260-519457) objects, 0(0) handoff, 0(0) steal, 0/0/0 yields

可以看出,gc回收非常迅速(0ms),並且記憶體使用量也由原來gc後的31M 減少到12M。最佳化效果是很明顯的。

DealMap的實現

type DealMap struct {    table   []DealTiny    buckets int    size    int}// round 到最近的2的倍數func minBuckets(v int) int {    v--    v |= v >> 1    v |= v >> 2    v |= v >> 4    v |= v >> 8    v |= v >> 16    v++    return v}func hashInt32(x int) int {    x = ((x >> 16) ^ x) * 0x45d9f3b    x = ((x >> 16) ^ x) * 0x45d9f3b    x = ((x >> 16) ^ x)    return x}func NewDealMap(maxsize int) *DealMap {    buckets := minBuckets(maxsize)    return &DealMap{size: 0, buckets: buckets, table: make([]DealTiny, buckets)}}// TODO rehash策略func (m *DealMap) Put(d DealTiny) {    num_probes, bucket_count_minus_one := 0, m.buckets-1    bucknum := hashInt32(int(d.Dealid)) & bucket_count_minus_one    for {        if m.table[bucknum].Dealid == 0 { // insert, 不支援放入ID為0的Deal            m.size += 1            m.table[bucknum] = d            return        }        if m.table[bucknum].Dealid == d.Dealid { // update            m.table[bucknum] = d            return        }        num_probes += 1 // Open addressing with Linear probing         bucknum = (bucknum + num_probes) & bucket_count_minus_one    }}func (m *DealMap) Get(id int32) (DealTiny, bool) {    num_probes, bucket_count_minus_one := 0, m.buckets-1    bucknum := hashInt32(int(id)) & bucket_count_minus_one    for {        if m.table[bucknum].Dealid == id {            return m.table[bucknum], true        }        if m.table[bucknum].Dealid == 0 {            return m.table[bucknum], false        }        num_probes += 1        bucknum = (bucknum + num_probes) & bucket_count_minus_one    }}
相關文章

聯繫我們

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