测试代码:

package main

import (
	"fmt"
	"github.com/tidwall/shardmap"
	"math/rand"
	"strconv"
	"sync"
	"time"
)

func main() {
	SynTest()
	ConCurrentMap()
}

func SynTest() {
	var m sync.Map
	m.Store("count", 0)

	var wg sync.WaitGroup
	start := time.Now()

	ranRange := 10 * 1000000
	rand.Seed(time.Now().UnixNano())

	for numOfThread := 0; numOfThread < 10; numOfThread++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for i := 0; i < 1000000; i++ {
				randomNum := rand.Intn(ranRange)
				val := strconv.Itoa(randomNum)

				m.Store(val, val)
				randomNum = rand.Intn(ranRange)
				val = strconv.Itoa(randomNum)
				m.Load(val)
			}
		}()
	}
	wg.Wait()

	t := time.Now()
	elapsed := t.Sub(start)
	fmt.Println("sync map elapsed :", elapsed)

}

func  ConCurrentMap() {
	 m := shardmap.New(1000000 )

	var wg sync.WaitGroup
	start := time.Now()

	ranRange := 10 * 1000000
	rand.Seed(time.Now().UnixNano())

	for numOfThread := 0; numOfThread < 10; numOfThread++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for i := 0; i < 1000000; i++ {
				randomNum := rand.Intn(ranRange)
				val := strconv.Itoa(randomNum)

				m.Set(val, val)
				randomNum = rand.Intn(ranRange)
				val = strconv.Itoa(randomNum)
				m.Get(val)
			}
		}()
	}
	wg.Wait()

	t := time.Now()
	elapsed := t.Sub(start)
	fmt.Println("ConCurrent Map elapsed :", elapsed)

}

与sync.Map对比结果如下:

sync map elapsed : 14.6189622s
ConCurrent Map elapsed : 4.8839917s

Process finished with the exit code 0

shardmap的性能比golang自带的性能高2倍左右。

其关键实现主要在initDo函数的实现:

源码如下:

func (m *Map) initDo() {
	m.init.Do(func() {//这里函数只会执行一次,主要是初始化
		m.shards = 1
		for m.shards < runtime.NumCPU()*16 {//获取运行时的CPU数量,我的测试机是8,也就是m.shards最终为128
			m.shards *= 2
		}
		scap := m.cap / m.shards//根据这个数值,计算每个哈希表的容量,7812
		m.mus = make([]sync.RWMutex, m.shards)//为每个哈希表分配一把读写锁
		m.maps = make([]*rhh.Map, m.shards)//分配128个哈希表
		for i := 0; i < len(m.maps); i++ {
			m.maps[i] = rhh.New(scap)//根据前面的计算,为这128个哈希表分配7812的容量,以2的倍数向上取整,最小为8,运行期间,如果空间不够,会自动扩容,2倍的方式增加
		}
	})
}

//map结构定义
// Map is a hashmap. Like map[string]interface{}, but sharded and thread-safe.
type Map struct {
	init   sync.Once//只会执行一次
	cap    int
	shards int
	seed   uint32
	mus    []sync.RWMutex
	maps   []*rhh.Map
}

我们来看下set的实现:

// Set assigns a value to a key.
// Returns the previous value, or false when no value was assigned.
func (m *Map) Set(key string, value interface{}) (prev interface{}, replaced bool) {
	m.initDo()//只会执行一次,
	shard := m.choose(key)//计算当前的key倍映射到那个子哈希表中,
	m.mus[shard].Lock()//使用对应子哈希表的读写锁进行并发控制
	prev, replaced = m.maps[shard].Set(key, value)//插入或替换之前的内容,如果空间不够,执行resize
	m.mus[shard].Unlock()//解锁
	return prev, replaced//返回旧值
}

上面的分析,可以看到,同一个CPU冲突的概率为1/16,大大减小了锁的争抢。性能会高于sync.Map。这个库的代码比较简单,如果需要,定制开发,改起来也比较容易。如果想缩小概率也很容易修改。