goalng1.14不同場景下sync.Map壳澳、Mutex、RWMutex鎖性能測試對比

因?yàn)槭褂胓o過程中會經(jīng)常使用map存儲茫经,多個(gè)goroutine會出現(xiàn)資源競爭的問題巷波,所以寫個(gè)demo測試一下不同方式的鎖性能開銷。

1.環(huán)境

MacBook Pro (15-inch, 2016)
2.6 GHz Intel Core i7
16 GB 2133 MHz LPDDR3

golang版本:1.14

2.代碼

var (
    num  = 1000 * 10
    gnum = 1000
)
func Test_main(t *testing.T) {
    count := 10000
    div := int(50) //抽樣寫比例 1/5
    fmt.Println("only read")
    testRwmutexReadOnly(count)
    testMutexReadOnly(count)
    //test sync.map
    testSyncMapReadOnly(count)

    fmt.Println("write and read")
    testRwmutexWriteRead(count, div)
    testMutexWriteRead(count, div)
    testSyncMapWriteRead(count, div)

    fmt.Println("write only")
    testRwmutexWriteOnly(count)
    testMutexWriteOnly(count)
    testSyncMapWriteOnly(count)

}

func testRwmutexReadOnly(count int) {
    var w = &sync.WaitGroup{}
    var rwmutexTmp = newRwmutex(count)
    w.Add(gnum)
    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        go func() {
            defer w.Done()
            for in := 0; in < num; in++ {
                rwmutexTmp.get(in)
            }
        }()
    }
    w.Wait()
    fmt.Println("testRwmutexReadOnly cost:", time.Now().Sub(t1).String())
}

func testRwmutexWriteOnly(count int) {
    var w = &sync.WaitGroup{}
    var rwmutexTmp = newRwmutex(count)
    w.Add(gnum)
    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        go func() {
            defer w.Done()
            for in := 0; in < num; in++ {
                rwmutexTmp.set(in, in)
            }
        }()
    }
    w.Wait()
    fmt.Println("testRwmutexWriteOnly cost:", time.Now().Sub(t1).String())
}

func testRwmutexWriteRead(count, div int) {
    var w = &sync.WaitGroup{}
    var rwmutexTmp = newRwmutex(count)
    w.Add(gnum)
    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        if i%div != 0 {
            go func() {
                defer w.Done()
                for in := 0; in < num; in++ {
                    rwmutexTmp.get(in)
                }
            }()
        } else {
            go func() {
                defer w.Done()
                for in := 0; in < num; in++ {
                    rwmutexTmp.set(in, in)
                }
            }()
        }
    }
    w.Wait()
    fmt.Println("testRwmutexWriteRead cost:", time.Now().Sub(t1).String())
}

func testMutexReadOnly(count int) {
    var w = &sync.WaitGroup{}
    var mutexTmp = newMutex(count)
    w.Add(gnum)

    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        go func() {
            defer w.Done()
            for in := 0; in < num; in++ {
                mutexTmp.get(in)
            }
        }()
    }
    w.Wait()
    fmt.Println("testMutexReadOnly cost:", time.Now().Sub(t1).String())
}

func testMutexWriteOnly(count int) {
    var w = &sync.WaitGroup{}
    var mutexTmp = newMutex(count)
    w.Add(gnum)

    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        go func() {
            defer w.Done()
            for in := 0; in < num; in++ {
                mutexTmp.set(in, in)
            }
        }()
    }
    w.Wait()
    fmt.Println("testMutexWriteOnly cost:", time.Now().Sub(t1).String())
}

func testMutexWriteRead(count, div int) {
    var w = &sync.WaitGroup{}
    var mutexTmp = newMutex(count)
    w.Add(gnum)
    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        if i%div != 0 {
            go func() {
                defer w.Done()
                for in := 0; in < num; in++ {
                    mutexTmp.get(in)
                }
            }()
        } else {
            go func() {
                defer w.Done()
                for in := 0; in < num; in++ {
                    mutexTmp.set(in, in)
                }
            }()
        }

    }
    w.Wait()
    fmt.Println("testMutexWriteRead cost:", time.Now().Sub(t1).String())
}

func testSyncMapReadOnly(count int) {
    var w = &sync.WaitGroup{}
    var mutexTmp = newSyncMap(count)
    w.Add(gnum)

    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        go func() {
            defer w.Done()
            for in := 0; in < num; in++ {
                mutexTmp.Load(in)
            }
        }()
    }
    w.Wait()
    fmt.Println("testSyncMapReadOnly cost:", time.Now().Sub(t1).String())
}

func testSyncMapWriteOnly(count int) {
    var w = &sync.WaitGroup{}
    var mutexTmp = newSyncMap(count)
    w.Add(gnum)

    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        go func() {
            defer w.Done()
            for in := 0; in < num; in++ {
                mutexTmp.Store(in, in)
            }
        }()
    }
    w.Wait()
    fmt.Println("testSyncMapWriteOnly cost:", time.Now().Sub(t1).String())
}

func testSyncMapWriteRead(count, div int) {
    var w = &sync.WaitGroup{}
    var mutexTmp = newSyncMap(count)
    w.Add(gnum)
    t1 := time.Now()
    for i := 0; i < gnum; i++ {
        if i%div != 0 {
            go func() {
                defer w.Done()
                for in := 0; in < num; in++ {
                    mutexTmp.Load(in)
                }
            }()
        } else {
            go func() {
                defer w.Done()
                for in := 0; in < num; in++ {
                    mutexTmp.Store(in, in)
                }
            }()
        }

    }
    w.Wait()
    fmt.Println("testSyncMapWriteRead cost:", time.Now().Sub(t1).String())
}

func newRwmutex(count int) *rwmutex {
    var t = &rwmutex{}
    t.mu = &sync.RWMutex{}
    t.ipmap = make(map[int]int, count)

    for i := 0; i < count; i++ {
        t.ipmap[i] = 0
    }
    return t
}

type rwmutex struct {
    mu    *sync.RWMutex
    ipmap map[int]int
}

func (t *rwmutex) get(i int) int {
    t.mu.RLock()
    defer t.mu.RUnlock()

    return t.ipmap[i]
}

func (t *rwmutex) set(k, v int) {
    t.mu.Lock()
    defer t.mu.Unlock()

    t.ipmap[k] = v
}

func newMutex(count int) *mutex {
    var t = &mutex{}
    t.mu = &sync.Mutex{}
    t.ipmap = make(map[int]int, count)

    for i := 0; i < count; i++ {
        t.ipmap[i] = 0
    }
    return t
}

func newSyncMap(count int) *sync.Map {
    var t = &sync.Map{}

    for i := 0; i < count; i++ {
        t.Store(i, 0)
    }
    return t
}

type mutex struct {
    mu    *sync.Mutex
    ipmap map[int]int
}

func (t *mutex) get(i int) int {
    t.mu.Lock()
    defer t.mu.Unlock()

    return t.ipmap[i]
}

func (t *mutex) set(k, v int) {
    t.mu.Lock()
    defer t.mu.Unlock()

    k = k % 100
    t.ipmap[k] = v
}

3.測試結(jié)果

//測試結(jié)果
//only read
testRwmutexReadOnly cost: 506.182734ms
testMutexReadOnly cost: 1.970860548s
testSyncMapReadOnly cost: 113.849084ms

//write and read
testRwmutexWriteRead cost: 1.827954707s
testMutexWriteRead cost: 2.074191088s
testSyncMapWriteOnly cost: 4.387804708s

//抽樣寫比例 1/5
//write and read
testRwmutexWriteRead cost: 1.139143888s
testMutexWriteRead cost: 1.965517324s
testSyncMapWriteRead cost: 188.517601ms

//抽樣寫比例 1/50
//write and read
testRwmutexWriteRead cost: 809.852228ms
testMutexWriteRead cost: 1.903433116s
testSyncMapWriteRead cost: 133.22511ms

//write only
testRwmutexWriteOnly cost: 2.917429869s
testMutexWriteOnly cost: 2.245351033s
testSyncMapWriteRead cost: 315.201658ms

4.結(jié)論

只讀場景:sync.map > rwmutex >> mutex
讀寫場景(邊讀邊寫):rwmutex > mutex >> sync.map
讀寫場景(讀80% 寫20%):sync.map > rwmutex > mutex
讀寫場景(讀98% 寫2%):sync.map > rwmutex >> mutex
只寫場景:sync.map >> mutex > rwmutex

5.個(gè)人建議和使用習(xí)慣

一般來講大部分面臨的都是讀寫場景卸伞,關(guān)鍵在于讀寫的比例褥紫。
考慮到編碼和代碼可讀性,如果存儲復(fù)雜結(jié)構(gòu)體瞪慧,我仍然傾向于rwmutex髓考;如果是簡單結(jié)構(gòu)類型,我傾向選擇sync.map弃酌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氨菇,一起剝皮案震驚了整個(gè)濱河市儡炼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌查蓉,老刑警劉巖乌询,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異豌研,居然都是意外死亡妹田,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門鹃共,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鬼佣,“玉大人,你說我怎么就攤上這事霜浴【е裕” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵阴孟,是天一觀的道長晌纫。 經(jīng)常有香客問我,道長永丝,這世上最難降的妖魔是什么锹漱? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮慕嚷,結(jié)果婚禮上哥牍,老公的妹妹穿的比我還像新娘。我一直安慰自己闯冷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布懈词。 她就那樣靜靜地躺著蛇耀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坎弯。 梳的紋絲不亂的頭發(fā)上纺涤,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機(jī)與錄音抠忘,去河邊找鬼撩炊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛崎脉,可吹牛的內(nèi)容都是我干的拧咳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼囚灼,長吁一口氣:“原來是場噩夢啊……” “哼骆膝!你這毒婦竟也來了祭衩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤阅签,失蹤者是張志新(化名)和其女友劉穎掐暮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體政钟,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡路克,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了养交。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片精算。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖层坠,靈堂內(nèi)的尸體忽然破棺而出殖妇,到底是詐尸還是另有隱情,我是刑警寧澤破花,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布谦趣,位于F島的核電站,受9級特大地震影響座每,放射性物質(zhì)發(fā)生泄漏前鹅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一峭梳、第九天 我趴在偏房一處隱蔽的房頂上張望舰绘。 院中可真熱鬧,春花似錦葱椭、人聲如沸捂寿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秦陋。三九已至,卻和暖如春治笨,著一層夾襖步出監(jiān)牢的瞬間驳概,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工旷赖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顺又,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓等孵,卻偏偏與公主長得像稚照,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容