如何給Golang的gc減負

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

這篇文章想聊聊如何給gc減負的問題,也即我們在寫業務的時候,有時候需要考慮下gc老人家的感受,但又不能喪失代碼的可讀性,有些情況下代碼需不需要最佳化,最佳化後能取得多大的效能提升,其中的平衡需要把握。不然很容易出現:脫褲子放屁,多此一舉。

  1. 字串拼接,demo代碼:
package mainimport (    "bytes")func f1(l int) {    var s, s1 string = ``, `hello world`    for i := 0; i < l; i++ {        s = s + s1    }}func f2(l int) {    buf := bytes.NewBuffer([]byte{})    var s1 string = `hello world`    for i := 0; i < l; i++ {        buf.WriteString(s1)    }}func f3(l int) {    var s []string    var s1 string = `hello world`    for i := 0; i < l; i++ {        s = append(s, s1)    }    strings.Join(s, ``)}

測試代碼:

package mainimport (    "testing")func Benchmark_F1(b *testing.B) {    for i := 0; i < b.N; i++ {        f1(100000)    }}func Benchmark_F2(b *testing.B) {    for i := 0; i < b.N; i++ {        f2(100000)    }}func Benchmark_F3(b *testing.B) {    for i := 0; i < b.N; i++ {        f3(100000)    }}

go test -bench=".*" -test.benchmem -count=1 輸出的結果是:

Benchmark_F1-4             1    9334113815 ns/op    55401130720 B/op      100056 allocs/opBenchmark_F2-4          1000       2318146 ns/op     2891600 B/op         18 allocs/opBenchmark_F3-4           100      14660804 ns/op    11459184 B/op         32 allocs/opPASSok      _/Users/taomin/pprof/string 13.394s

從bench的結果來看,10W個字串的拼接,+的最差,bytes.NewBuffe最好。記憶體和時間上的差距大概在3個數量級左右。這種字串拼接的最佳化,除非你的情境確實存在大量字串拼接,不然不要使用什麼bytes或者strings.join,直接+拼接起來就好了。

  1. 臨時對象池
    對象池將對象存放到池裡,通過複用之前的對象,從而減少指派至的個數。每次GC,runtime會調用poolCleanup函數來將Pool清空,這樣原本Pool中儲存的對象會被GC全部回收。這是Pool的一個特性,這個特性會導致:有狀態的對象不能儲存在Pool中,Pool不能用作串連池;官方文檔說明如下:
A Pool is a set of temporary objects that may be individually saved and retrieved.Any item stored in the Pool may be removed automatically at any time without notification. If the Pool holds the only reference when this happens, the item might be deallocated.A Pool is safe for use by multiple goroutines simultaneously.Pool's purpose is to cache allocated but unused items for later reuse, relieving pressure on the garbage collector. That is, it makes it easy to build efficient, thread-safe free lists. However, it is not suitable for all free lists.An appropriate use of a Pool is to manage a group of temporary items silently shared among and potentially reused by concurrent independent clients of a package. Pool provides a way to amortize allocation overhead across many clients.An example of good use of a Pool is in the fmt package, which maintains a dynamically-sized store of temporary output buffers. The store scales under load (when many goroutines are actively printing) and shrinks when quiescent.On the other hand, a free list maintained as part of a short-lived object is not a suitable use for a Pool, since the overhead does not amortize well in that scenario. It is more efficient to have such objects implement their own free list.A Pool must not be copied after first use.type Pool struct {        // New optionally specifies a function to generate        // a value when Get would otherwise return nil.        // It may not be changed concurrently with calls to Get.        New func() interface{}        // contains filtered or unexported fields}

在網上有哥們寫的Pool的例子,覺得很能說明問題,這裡用他的代碼作為例子來說明:

package mainimport (    "fmt"    "io"    "net/http"    "sync")// 並發過程使用了多少次 []bytevar mu sync.Mutexvar holder map[string]bool = make(map[string]bool)// 臨時對象池var p = sync.Pool{    New: func() interface{} {        buffer := make([]byte, 1024)        return &buffer    },}func readContent(wg *sync.WaitGroup) {    defer wg.Done()    resp, err := http.Get("http://my.oschina.net/xinxingegeya/home")    if err != nil {        fmt.Println(err)    }    defer resp.Body.Close()    byteSlice := p.Get().(*[]byte) //類型斷言    key := fmt.Sprintf("%p", byteSlice)    mu.Lock()    _, ok := holder[key]    if !ok {        holder[key] = true    }    mu.Unlock()    _, err = io.ReadFull(resp.Body, *byteSlice)    if err != nil {        fmt.Println(err)    }    p.Put(byteSlice)}func main() {    var wg sync.WaitGroup    for i := 0; i < 10; i++ {        wg.Add(1)        go readContent(&wg)    }    wg.Wait()    for key, val := range holder {        fmt.Println("Key:", key, "Value:", val)    }}

Pool要慎用哦,因為它的脾氣不太好。

  1. string轉位元組數組
    string類型的變數裡面的值會被複製到位元組數組中,雨虹學堂上之前一種最佳化思路,通過unsafe包的指標操作,直接對string的地址進行操作,這樣避免了記憶體複製操作,但個人感覺這個最佳化有點過了,這會讓代碼失去可讀性,例如:
s := "hello world!"b := []byte(s)

被改成了:

 func str2bytes(s string) []byte {    x := (*[2]uintptr)(unsafe.Pointer(&s))    h := [3]uintptr{x[0], x[1], x[1]}    return *(*[]byte)(unsafe.Pointer(&h))}

下面的代碼如果不好好研究下,完全看不懂在搞神馬。以上這些是自己能想到的最佳化點,你有什麼好的最佳化方案和經曆,歡迎分享。。。
end ~

聯繫我們

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