2020年5月21日 星期四

golang channel buffered vs unbuffered

buffered vs unbuffered channel 有些網路上文章以 同步與異步來解釋,雖然感覺上有同步與異步,但其並非同步與異步。非常單純就只是有沒有多buffer空間

先看一個案例
func main() {
    c := make(chan bool, 1)
    go func() {
        fmt.Println("go func()", <-c)
        fmt.Println("go func()", <-c)
    }()

    c <- true
    time.Sleep(1 * time.Second)

    c <- false
    c <- true

    fmt.Println("main()")

}
執行結果
go func() true
... (sleep 1 sec) ...
go func() false
main()

就寫入channel而言,只要還有buffer,就在寫入後立即往下執行,反之,會等待有多餘的空間。因此有同步的感覺,事實上,程式仍是在不同的協程中以非同步進行。

就讀出channel而言,有資料就會讀出後立即往下執行,反之,會等待有資料才讀出往下執行。

所以別再以同步或非同步(異步)來理解channel。而是以 阻塞(blocked)來理解才是正確的唷!

另外加以下二個思考題,請大家自行測試,

channel宣告改為以下buffer size = 2 ,執行結果如何?
    c := make(chan bool, 2)
channel宣告的buffer size = 0,大家覺得成立嗎,還是會有error呢?
    c := make(chan bool, 0)

希望以上的解說,能導正對channel的認知

2020年3月10日 星期二

程式效能之路(1) 變數宣告的時機影響記憶體分配的次數

情境: 每次loop都需要新的變數來進行運算,且為確保每次運算獨立,所以在每進入loop就重新宣告(配置)變數。


func () test() {
    for i := 0; i < 1000000000; i++ {
        var (
            v11, v12, v13, v21, v22, v23 float64
        )
        //...
        //...
        //...
    }
}
這樣看似平常,但在每次loop都會進行記憶體分配與釋放,都是很花費cpu time。


所以應調整成如下:
func () test() {
    var (
        v11, v12, v13, v21, v22, v23 float64

        initVar = func() {
            v11, v12, v13, v21, v22, v23 = 0, 0, 0, 0, 0, 0
        }
    )

    for i := 0; i < 1000000000; i++ {
        initVar()
        //...
        //...
        //...
    }
}
只有進loop時做宣告(配置)變數,而在每次loop開始將變數初始化

golang inner func() 讓code變優雅變精巧


原始 for loop裡在進入時會進行許多的資料判別是否可以往下運行。程式看起來非常的冗長
func (a *ArbTriangular) stg() {
    var (
        askDetph   map[string]db.Order10Depth
        lastTicker map[string]db.LastTicker
    )
    // ...
    // ...
    // ...
    for _, st := range *a.sTriangular {
        if st.isTrading {
            continue
        }
        if _, b := askDetph[st.a]; !b {
            continue
        }
        if _, b := askDetph[st.b]; !b {
            continue
        }
        if _, b := askDetph[st.c]; !b {
            continue
        }
        if _, b := lastTicker[st.a]; !b {
            continue
        }
        if _, b := lastTicker[st.b]; !b {
            continue
        }
        if _, b := lastTicker[st.c]; !b {
            continue
        }
        // ...
        // ...
        // ...
    }
}

進行一次的重構,for loop 看起來好多了,但是冗長的if判別看起來還是不夠優雅
func (a *ArbTriangular) stg() {
    var (
        askDetph map[string]db.Order10Depth
        bidDetph map[string]db.Order10Depth
    )
    checkData := func(st SymboleTriangular) (b bool) {
        if st.isTrading {
            return
        }
        if _, b = askDetph[st.a]; !b {
            return
        }
        if _, b = askDetph[st.b]; !b {
            return
        }
        if _, b = askDetph[st.c]; !b {
            return
        }
        if _, b = bidDetph[st.a]; !b {
            return
        }
        if _, b = bidDetph[st.b]; !b {
            return
        }
        if _, b = bidDetph[st.c]; !b {
            return
        }
        b = true
        return
    }
    //...
    //...
    //...
    for _, st := range *a.sTriangular {
        if !checkData(st) {
            continue
        }
        //...
        //...
        //...
    }
}

進行第二次的重構,這樣的成果優雅又精簡巧
func (a *ArbTriangular) stg() {
    var (
        askDetph map[string]db.Order10Depth
        bidDetph map[string]db.Order10Depth

        existAsk = func(s string) (b bool) {
            _, b = askDetph[s]
            return
        }
        existBid = func(s string) (b bool) {
            _, b = bidDetph[s]
            return
        }
        checkData = func(st SymboleTriangular) bool {
            if st.isTrading ||
                !existAsk(st.a) || !existAsk(st.b) || !existAsk(st.c) ||
                !existBid(st.a) || !existBid(st.b) || !existBid(st.c) {
                return false
            }
            return true
        }
    )
    //...
    //...
    //...
    for _, st := range *a.sTriangular {
        if !checkData(st) {
            continue
        }
        //...
        //...
        //...
    }
}