go 分段锁ConcurrentMap,map+读写锁,sync.map的效率测试
生活随笔
收集整理的這篇文章主要介紹了
go 分段锁ConcurrentMap,map+读写锁,sync.map的效率测试
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
分段鎖ConcurrentMap的實現(xiàn)請參考筆者的另外一篇博客:
go 分段鎖ConcurrentMap的實現(xiàn)源碼
效率測試結(jié)論:
1. go自帶的map不是多協(xié)程安全的
2. 分段鎖ConcurrentMap是多協(xié)程安全的,且效率最高;sync.map效率次之,傳統(tǒng)的map+讀寫鎖效率最低
3.?ConcurrentMap中鎖的個數(shù)越多,效率越高,因為爭奪同一把鎖的概率降低了
測試流程:
1. 產(chǎn)生1萬個key-value對,加入到各個測試map中
2. 開始計時
3. 開啟兩個協(xié)程,一個寫協(xié)程隨機賦值10萬次,一個讀協(xié)程隨機訪問10萬次
4. 結(jié)束計時,打印出耗時,內(nèi)存操作等信息
測試結(jié)果如下圖(紅框處是每次測試的耗時):
源碼如下:
測試的源碼main_test.go
package mainimport ("math/rand""strconv""sync""testing" )// 10萬次的賦值,10萬次的讀取 var times int = 100000// 測試ConcurrentMap func BenchmarkTestConcurrentMap(b *testing.B) {for k := 0; k < b.N; k++ {b.StopTimer()// 產(chǎn)生10000個不重復(fù)的鍵值對(string -> int)testKV := map[string]int{}for i := 0; i < 10000; i++ {testKV[strconv.Itoa(i)] = i}// 新建一個ConcurrentMappMap := NewConcurrentMap()// set到map中for k, v := range testKV {pMap.Set(k, v)}// 開始計時b.StartTimer()wg := sync.WaitGroup{}wg.Add(2)// 賦值go func() {// 對隨機key,賦值times次for i := 0; i < times; i++ {index := rand.Intn(times)pMap.Set(strconv.Itoa(index), index+1)}wg.Done()}()// 讀取go func() {// 對隨機key,讀取times次for i := 0; i < times; i++ {index := rand.Intn(times)pMap.Get(strconv.Itoa(index))}wg.Done()}()// 等待兩個協(xié)程處理完畢wg.Wait()} }// 測試map加鎖 func BenchmarkTestMap(b *testing.B) {for k := 0; k < b.N; k++ {b.StopTimer()// 產(chǎn)生10000個不重復(fù)的鍵值對(string -> int)testKV := map[string]int{}for i := 0; i < 10000; i++ {testKV[strconv.Itoa(i)] = i}// 新建一個MutexMappMap := NewMutexMap()// set到map中for k, v := range testKV {pMap.Set(k, v)}// 開始計時b.StartTimer()wg := sync.WaitGroup{}wg.Add(2)// 賦值go func() {// 對隨機key,賦值100萬次for i := 0; i < times; i++ {index := rand.Intn(times)pMap.Set(strconv.Itoa(index), index+1)}wg.Done()}()// 讀取go func() {// 對隨機key,讀取100萬次for i := 0; i < times; i++ {index := rand.Intn(times)pMap.Get(strconv.Itoa(index))}wg.Done()}()// 等待兩個協(xié)程處理完畢wg.Wait()} }// 測試sync.map func BenchmarkTestSyncMap(b *testing.B) {for k := 0; k < b.N; k++ {b.StopTimer()// 產(chǎn)生10000個不重復(fù)的鍵值對(string -> int)testKV := map[string]int{}for i := 0; i < 10000; i++ {testKV[strconv.Itoa(i)] = i}// 新建一個sync.MappMap := &sync.Map{}// set到map中for k, v := range testKV {pMap.Store(k, v)}// 開始計時b.StartTimer()wg := sync.WaitGroup{}wg.Add(2)// 賦值go func() {// 對隨機key,賦值10萬次for i := 0; i < times; i++ {index := rand.Intn(times)pMap.Store(strconv.Itoa(index), index+1)}wg.Done()}()// 讀取go func() {// 對隨機key,讀取10萬次for i := 0; i < times; i++ {index := rand.Intn(times)pMap.Load(strconv.Itoa(index))}wg.Done()}()// 等待兩個協(xié)程處理完畢wg.Wait()} }分段鎖map的實現(xiàn)源碼concurrentmap.go
package mainimport ("sync" )// 總的map type ConcurrentMap []*ConcurrentMapShared// 默認分片數(shù) const SHARE_COUNT int = 64// 單個map分片 type ConcurrentMapShared struct {items map[string]interface{} // 本分片內(nèi)的mapmu sync.RWMutex // 本分片的專用鎖 }// 新建一個map func NewConcurrentMap() *ConcurrentMap {m := make(ConcurrentMap, SHARE_COUNT)for i := 0; i < SHARE_COUNT; i++ {m[i] = &ConcurrentMapShared{items: map[string]interface{}{},}}return &m }// GetSharedMap 獲取key對應(yīng)的map分片 func (m ConcurrentMap) GetSharedMap(key string) *ConcurrentMapShared {return m[uint(fnv32(key))%uint(SHARE_COUNT)] }// hash函數(shù) func fnv32(key string) uint32 {hash := uint32(2166136261)prime32 := uint32(16777619)for i := 0; i < len(key); i++ {hash *= prime32hash ^= uint32(key[i])}return hash }// Set 設(shè)置key,value func (m ConcurrentMap) Set(key string, value interface{}) {sharedMap := m.GetSharedMap(key) // 找到對應(yīng)的分片mapsharedMap.mu.Lock() // 加鎖(全鎖定)sharedMap.items[key] = value // 賦值sharedMap.mu.Unlock() // 解鎖 }// Get 獲取key對應(yīng)的value func (m ConcurrentMap) Get(key string) (value interface{}, ok bool) {sharedMap := m.GetSharedMap(key) // 找到對應(yīng)的分片mapsharedMap.mu.RLock() // 加鎖(讀鎖定)value, ok = sharedMap.items[key] // 取值sharedMap.mu.RUnlock() // 解鎖return value, ok }// Count 統(tǒng)計key個數(shù) func (m ConcurrentMap) Count() int {count := 0for i := 0; i < SHARE_COUNT; i++ {m[i].mu.RLock() // 加鎖(讀鎖定)count += len(m[i].items)m[i].mu.RUnlock() // 解鎖}return count }// Keys1 所有的key方法1(方法:遍歷每個分片map,讀取key;缺點:量大時,阻塞時間較長) func (m ConcurrentMap) Keys1() []string {count := m.Count()keys := make([]string, count)// 遍歷所有的分片mapfor i := 0; i < SHARE_COUNT; i++ {m[i].mu.RLock() // 加鎖(讀鎖定)oneMapKeys := make([]string, len(m[i].items))for k := range m[i].items {oneMapKeys = append(oneMapKeys, k)}m[i].mu.RUnlock() // 解鎖// 匯總到keyskeys = append(keys, oneMapKeys...)}return keys }// Keys2 所有的key方法2(方法:開多個協(xié)程分別對分片map做統(tǒng)計再匯總 優(yōu)點:量大時,阻塞時間較短) func (m ConcurrentMap) Keys2() []string {count := m.Count()keys := make([]string, count)ch := make(chan string, count) // 通道,遍歷時// 單獨起一個協(xié)程go func() {wg := sync.WaitGroup{}wg.Add(SHARE_COUNT)for i := 0; i < SHARE_COUNT; i++ {// 每個分片map,單獨起一個協(xié)程進行統(tǒng)計go func(ms *ConcurrentMapShared) {defer wg.Done()ms.mu.RLock() // 加鎖(讀鎖定)for k := range ms.items {ch <- k // 壓入通道}ms.mu.RUnlock() // 解鎖}(m[i])}// 等待所有協(xié)程執(zhí)行完畢wg.Wait()close(ch) // 一定要關(guān)閉通道,因為不關(guān)閉的話,后面的range不會結(jié)束!!!}()// 遍歷通道,壓入所有的keyfor k := range ch {keys = append(keys, k)}return keys }map+讀寫鎖的實現(xiàn)源碼mutexmap.go
package mainimport ("sync" )// 對外暴露的map type MutexMap struct {items map[string]interface{} // 為了和上面的ConcurrentMap做比較,都采用string->interface的方式mu *sync.RWMutex // 讀寫鎖 }// 新建一個map func NewMutexMap() *MutexMap {return &MutexMap{items: map[string]interface{}{},mu: new(sync.RWMutex),} }// Set 設(shè)置key,value func (m MutexMap) Set(key string, value interface{}) {m.mu.Lock() // 加鎖(全鎖定)m.items[key] = value // 賦值m.mu.Unlock() // 解鎖 }// Get 獲取key對應(yīng)的value func (m MutexMap) Get(key string) (value interface{}, ok bool) {m.mu.RLock() // 加鎖(讀鎖定)value, ok = m.items[key] // 取值m.mu.RUnlock() // 解鎖return value, ok }// Count 統(tǒng)計key個數(shù) func (m MutexMap) Count() int {m.mu.RLock() // 加鎖(讀鎖定)count := len(m.items)m.mu.RUnlock() // 解鎖return count }// Keys 所有的key func (m MutexMap) Keys() []string {m.mu.RLock() // 加鎖(讀鎖定)keys := make([]string, len(m.items))for k := range m.items {keys = append(keys, k)}m.mu.RUnlock() // 解鎖return keys }?
總結(jié)
以上是生活随笔為你收集整理的go 分段锁ConcurrentMap,map+读写锁,sync.map的效率测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2-3树的由来
- 下一篇: go 判断channel是否关闭