├── xmap ├── README ├── concurrent_hash_map_benchmark_test.go ├── xmap.go ├── concurrent_hash_map_test.go ├── map_test.go ├── entry │ └── rbtree.go └── concurrent_raw_hash_map.go ├── docs ├── README.md ├── img │ ├── README │ ├── xds02.png │ ├── xmap01.png │ ├── xmap02.png │ ├── xmap03.png │ ├── xmap04.png │ ├── xmap05.png │ └── xds-logo01.png └── Xmap-Implement.md ├── example ├── README.md ├── xmap_test1.go ├── xslice_test0.go └── xmap_test0.go ├── go.mod ├── .gitignore ├── xslice ├── xslice_test.go └── xslice.go ├── go.sum ├── common.go ├── README.md └── LICENSE /xmap/README: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/img/README: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/img/xds02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xds02.png -------------------------------------------------------------------------------- /docs/img/xmap01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xmap01.png -------------------------------------------------------------------------------- /docs/img/xmap02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xmap02.png -------------------------------------------------------------------------------- /docs/img/xmap03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xmap03.png -------------------------------------------------------------------------------- /docs/img/xmap04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xmap04.png -------------------------------------------------------------------------------- /docs/img/xmap05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xmap05.png -------------------------------------------------------------------------------- /docs/img/xds-logo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heiyeluren/xds/HEAD/docs/img/xds-logo01.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/heiyeluren/xds 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/heiyeluren/xmm v0.2.7 7 | github.com/spf13/cast v1.4.1 8 | ) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | .idea -------------------------------------------------------------------------------- /example/xmap_test1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/heiyeluren/xmm" 6 | "github.com/heiyeluren/xds" 7 | "github.com/heiyeluren/xds/xmap" 8 | ) 9 | 10 | //------------------ 11 | // xmap快速使用案例 12 | //------------------ 13 | 14 | func main() { 15 | 16 | //创建XMM内存块 17 | f := &xmm.Factory{} 18 | mm, err := f.CreateMemory(0.75) 19 | if err != nil { 20 | panic("error") 21 | } 22 | 23 | //用xmap创建一个map,结构类似于 map[string]uint,操作类似于 m: = make(map[string]uint) 24 | m, err := xmap.NewMap(mm, xds.String, xds.Int) 25 | if err != nil { 26 | panic("error") 27 | } 28 | // 调用Set()给xmap写入数据 29 | err = m.Set("k1", 1) 30 | err = m.Set("k2", 2) 31 | err = m.Set("k3", 3) 32 | err = m.Set("k4", 4) 33 | err = m.Set("k5", 5) 34 | 35 | // 调用Get()读取单个数据 36 | ret, exists, err := m.Get("k1") 37 | if exists == false { 38 | panic("key not found") 39 | } 40 | fmt.Printf("Get data key:[%s] value:[%s] \n", "k1", ret) 41 | 42 | // 使用Each()访问所有xmap元素 43 | // 在遍历操作中,让全局变量可以在匿名函数中访问(如果需要使用外部变量,可以像这样) 44 | gi := 1 45 | //调用 Each 遍历函数 46 | m.Each(func(key, val interface{}) error { 47 | //针对每个KV进行操作,比如打印出来 48 | fmt.Printf("For each XMap all key:[%s] value:[%s] \n", key, val) 49 | 50 | //外部变量使用操作 51 | gi++ 52 | return nil 53 | }) 54 | fmt.Printf("For each success, var[gi], val[%s]\n", gi); 55 | 56 | //使用Len()获取xmap元素总数量 57 | len := m.Len() 58 | fmt.Printf("Xmap item size:[%s]\n", len); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /example/xslice_test0.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | // "github.com/heiyeluren/xmm" 6 | "github.com/heiyeluren/xds" 7 | "github.com/heiyeluren/xds/xslice" 8 | ) 9 | 10 | //--------------------- 11 | // xslice快速使用案例 12 | //--------------------- 13 | 14 | func main() { 15 | 16 | fmt.Println("\n--[ Xslice API example]--\n") 17 | 18 | 19 | //创建XSlice对象, 类似于 s0 []Int64 = make([]Int64, 1) 20 | s0 := xslice.NewXslice(xds.Int,1) 21 | 22 | fmt.Println("\n### Xslice - Set/Get OP ###\n") 23 | 24 | //Set压入数据, 类似于 s0 :=[] int { 11, 22, 33 } 25 | s0.Set(1, 11) 26 | s0.Set(2, 22) 27 | s0.Set(3, 33) 28 | 29 | //Get读取一个数据 30 | vi,nil := s0.Get(1) 31 | fmt.Println("XSlice get data 1: ", vi) 32 | vi,nil = s0.Get(2) 33 | fmt.Println("XSlice get data 2: ", vi) 34 | vi,nil = s0.Get(3) 35 | fmt.Println("XSlice get data 3: ", vi) 36 | 37 | //读取整个slice长度, 类似于 len() 38 | fmt.Println("XSlice data size: ", s0.Len(), "\n") 39 | 40 | //释放资源 41 | s0.Free() 42 | 43 | fmt.Println("\n### Xslice - Append/ForEach OP ###\n") 44 | 45 | //批量压入Int ( 类似于 s1 []Int64 = make([]Int64, 1) ) 46 | s1 := xslice.NewXslice(xds.Int, 1) 47 | for i:=50; i<=55; i++ { 48 | s1.Append(i) 49 | } 50 | 51 | //fmt.Println(s1.Get(2)) 52 | 53 | //使用ForEach读取所有数据 54 | s1.ForEach(func(i int, v interface{}) error { 55 | fmt.Println("XSlice foreach data i: ",i, " v: ", v) 56 | return nil 57 | }) 58 | 59 | fmt.Println("\n### Xslice - Append/ForEach Data struct ###\n") 60 | fmt.Println(s1, "\n") 61 | 62 | ////读取整个slice长度, 类似于 len() 63 | fmt.Println("XSlice data size: ", s1.Len(), "\n") 64 | } 65 | -------------------------------------------------------------------------------- /docs/Xmap-Implement.md: -------------------------------------------------------------------------------- 1 | 2 | # Xds - XMap技术设计与实现 3 | 4 | ## 一、XMap 背景目标 5 | 6 | - 名词解释: 7 | ``` 8 | XMap - eXtensible Map Struct(高性能的第三方Map数据结构) 9 | Map - Golang原生map数据结构 10 | sync.Map - Golang原生并发map结构 11 | ``` 12 | 13 | #### XMap设计背景: 14 | 现有Golang中的map数据结构无法解决并发读写问题,Sync.map 并发性能偏差,针对这个情况,为XCache服务需要一个高性能、大容量、高并发、无GC的Map,所以开发实现 XMap。 15 | 针对我们需求调研了市场上主要的 hashmap 结构,不能满足我们性能和功能要求。 16 | 17 |
18 | 19 | #### XMap设计目标: 20 | 要求设计一个可以并发读写不会出现panic,要求并发读写 200w+ OPS/s 的并发map结构。 21 | (写20%,读80%场景;说明:go自带map读写性能在80w ops/s,大量并发读写下可能panic;sync.map 写入性能在 100w OPS/s) 22 | 23 |
24 |
25 | 26 | ## 二、XMap数据结构设计 27 |
28 | 29 | 为了保证读写性能的要求,同时保证内存的友好。我们采用了开链法方式来做为存储结构,如图为基本数据结构: 30 |
31 | 32 | ```go 33 | type ConcurrentHashMap struct { 34 | size uint64 //当前key的数量 35 | threshold uint64 // 扩容阈值 36 | initCap uint64 //初始化bulks大小 37 | 38 | //off-heap 39 | bulks *[]uintptr // 保存链表头结点指针的数组 40 | mm xmm.XMemory // 非堆的对象初始化器 41 | lock sync.RWMutex 42 | 43 | treeSize uint64 // 由链表转为红黑树的阈值 44 | 45 | //resize 46 | sizeCtl int64 // 状态: -1 正在扩容 47 | 48 | nextBulks *[]uintptr //正在扩容的目标Bulks 49 | transferIndex uint64 //扩容的索引 50 | } 51 | ``` 52 | 53 |
54 | 55 | 核心数据结构示例图: 56 | 57 | 58 | 59 | - 核心说明:使用key的hash值计算出数组索引,每个数组中保存一个链表或者红黑树头结点,找到所处的索引,利用尾插发追加节点。每个节点中有key、value等数据。 60 | 61 |
62 |
63 | 64 | ## 三、XMap内部实现流程图 65 | 66 | #### 3.1、Set 存储 67 | 68 | 69 |
70 | 71 | #### 3.2、Get 查询 72 | 73 |
74 | 75 | #### 3.3、Resize 扩容 76 | 77 |
78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /xslice/xslice_test.go: -------------------------------------------------------------------------------- 1 | package xslice 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | 7 | "github.com/heiyeluren/xds" 8 | 9 | ) 10 | 11 | func TestNewXslice(t *testing.T) { 12 | xs := NewXslice(xds.String,10) 13 | log.Println(xs.s) 14 | } 15 | 16 | func TestXslice_Append(t *testing.T) { 17 | xs2 := NewXslice(xds.Int,1) 18 | log.Println(xs2.s) 19 | 20 | for i:=0;i<=30;i++ { 21 | xs2.Append(i) 22 | } 23 | 24 | log.Println(xs2.s) 25 | } 26 | 27 | func TestXslice_Set(t *testing.T) { 28 | xs3 := NewXslice(xds.Int,1) 29 | log.Println(xs3.s) 30 | 31 | err := xs3.Set(1,123) 32 | if err != nil{ 33 | panic(err) 34 | } 35 | 36 | log.Println(xs3.s) 37 | 38 | } 39 | 40 | func TestXslice_Get(t *testing.T) { 41 | xs4 := NewXslice(xds.Int,1) 42 | err := xs4.Set(1,123) 43 | if err != nil{ 44 | panic(err) 45 | } 46 | 47 | v,err := xs4.Get(1) 48 | 49 | if err !=nil{ 50 | panic(err) 51 | } 52 | 53 | log.Println(v) 54 | } 55 | 56 | func TestXslice_Free(t *testing.T) { 57 | xs5 := NewXslice(xds.Int,1) 58 | err := xs5.Set(1,123) 59 | if err != nil{ 60 | panic(err) 61 | } 62 | 63 | log.Println(xs5) 64 | 65 | xs5.Free() 66 | 67 | log.Println(xs5) 68 | } 69 | 70 | func TestXslice_ForEach(t *testing.T) { 71 | xs6 := NewXslice(xds.String,1) 72 | xs6.Append("aaaa") 73 | xs6.Append("bbb") 74 | xs6.Append("cccc") 75 | xs6.Append("cccc") 76 | xs6.Append("cccc") 77 | xs6.Append("cccc") 78 | xs6.Append("cccc") 79 | xs6.Append("cccc") 80 | xs6.Append("cccc") 81 | xs6.Append("eee") 82 | 83 | 84 | xs6.ForEach(func(i int, v []byte) error { 85 | log.Println("i: ",i, "v: ", string(v)) 86 | return nil 87 | }) 88 | } 89 | 90 | func TestXslice_Len(t *testing.T) { 91 | xs8 := NewXslice(xds.Int,1) 92 | log.Println(xs8.s) 93 | 94 | err := xs8.Set(1,123) 95 | if err != nil{ 96 | panic(err) 97 | } 98 | 99 | log.Println(xs8.s) 100 | xs8.Set(2,123) 101 | xs8.Set(3,123) 102 | 103 | log.Println("len: ",xs8.Len()) 104 | } 105 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= 5 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 6 | github.com/heiyeluren/xmm v0.2.7 h1:jC3LX4bd7VezczzCJwDLHFQTUAfW9uVNzQZL0jNQNKk= 7 | github.com/heiyeluren/xmm v0.2.7/go.mod h1:l/H95AVDlcr0eGIbyGb7T7RVUPTKrGGK5knl75nsWk8= 8 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 9 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 10 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 11 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 12 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 16 | github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= 17 | github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 18 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 19 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 20 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 21 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 23 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 24 | -------------------------------------------------------------------------------- /xslice/xslice.go: -------------------------------------------------------------------------------- 1 | package xslice 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "sync" 7 | 8 | "github.com/heiyeluren/xds" 9 | ) 10 | 11 | var InvalidType = errors.New("type Error") // 类型错误 12 | 13 | type _gap []byte 14 | const _blockSize int = 8 15 | 16 | type Xslice struct{ 17 | s [][_blockSize]_gap //[]*uintptr 改为[]数组。数组内存放基本类型结构体和指针。string的话,存放指针。 18 | 19 | //s []uintptr uintptr地址指向一个连续内存(数组)的头。 20 | // s 的创建和扩容,通过xmm的alloc(元素个数*8)。迁移完后销毁。 21 | // s 中的元素创建:通过xmm的alloc(元素个数*元素单位长度)。append到s中。 22 | // 基本类型存放:int、uint、直接放入到s的元素中。 23 | // 非基本类型存放:string、[]byte,先拷贝内存到xmm中,然后,将指针append到s的元素中。 24 | 25 | lock *sync.RWMutex 26 | 27 | sizeCtl int64 // -1 正在扩容 28 | 29 | //游标位置 30 | curRPos int //读光标 31 | curWGapPos int //写gap光标 32 | curWSlotPos int //写slot光标 33 | 34 | _sizeof int //每个元素大小 35 | _stype xds.Kind //类型 36 | _len int //元素个数计数 37 | 38 | _scap int //容量 39 | } 40 | 41 | //func NewXslice(mm xmm.XMemory,_type Kind,_cap int) * Xslice { 42 | func NewXslice(_type xds.Kind,_cap int) *Xslice { 43 | 44 | //计算分配slot数量,有余数会多分配一个 45 | slotCap := float64(_cap % _blockSize) 46 | var slotNum int64 = int64(math.Ceil(slotCap)) 47 | 48 | if slotNum <= 0{ 49 | slotNum = 1 50 | } 51 | 52 | slot := make([][8]_gap,slotNum,slotNum) 53 | return &Xslice{ 54 | s: slot, 55 | lock: new(sync.RWMutex), 56 | _scap: _cap, 57 | _stype: _type, 58 | } 59 | } 60 | 61 | func(xs *Xslice) Append(v interface{}) (*Xslice,error) { 62 | xs.lock.Lock() 63 | defer xs.lock.Unlock() 64 | 65 | 66 | //判断类型:基本copy进去。 67 | // s中将要存放的uintptr拿到,uintptr为一个起始地址,加上一个index偏移量为写入地址。 68 | 69 | 70 | var gapPos int64 = 0 71 | 72 | slotCap := float64(xs.curWGapPos % _blockSize) 73 | 74 | gapPos = int64(slotCap) 75 | // 没有空位,扩容 76 | if slotCap == 0 && xs.curWGapPos >= _blockSize{ 77 | newSlot := [_blockSize]_gap{} 78 | xs.s = append(xs.s,newSlot) 79 | xs.curWSlotPos += 1 80 | } 81 | b,err := xds.Marshal(xs._stype,v) 82 | if err != nil{ 83 | return xs,err 84 | } 85 | 86 | xs.s[xs.curWSlotPos][gapPos] = b 87 | 88 | xs.curWGapPos += 1 89 | xs._len += 1 90 | 91 | return xs,nil 92 | } 93 | 94 | func (xs *Xslice) Set(n int,v interface{}) error { 95 | xs.lock.Lock() 96 | defer xs.lock.Unlock() 97 | 98 | var gapPos int64 = 0 99 | slotCap := float64(n % _blockSize) 100 | gapPos = int64(slotCap) 101 | 102 | // 没有空位,扩容 103 | if slotCap == 0 && xs.curWGapPos >= _blockSize{ 104 | newSlot := [_blockSize]_gap{} 105 | xs.s = append(xs.s,newSlot) 106 | xs.curWSlotPos += 1 107 | } 108 | 109 | b,err := xds.Marshal(xs._stype,v) 110 | if err != nil{ 111 | return err 112 | } 113 | 114 | xs.s[xs.curWSlotPos][gapPos] = b 115 | xs.curWGapPos += 1 116 | xs._len += 1 117 | 118 | return nil 119 | } 120 | 121 | func (xs *Xslice) Get(n int) (interface{}, error) { 122 | xs.lock.Lock() 123 | defer xs.lock.Unlock() 124 | 125 | var gapPos int64 = 0 126 | slotCap := float64(n % _blockSize) 127 | gapPos = int64(slotCap) 128 | b := xs.s[xs.curWSlotPos][gapPos] 129 | v,err := xds.UnMarshal(xs._stype,b) 130 | if err != nil{ 131 | return nil,err 132 | } 133 | 134 | return v,nil 135 | } 136 | 137 | func (xs *Xslice) Free () { 138 | xs.lock.Lock() 139 | xs.curWGapPos = 0 140 | xs.curRPos = 0 141 | xs._scap = 0 142 | xs._sizeof = 0 143 | xs._len = 0 144 | xs.curWSlotPos = 0 145 | xs.s = nil 146 | xs.lock.Unlock() 147 | } 148 | 149 | func(xs *Xslice) ForEach(f func(i int, v interface{}) error) error{ 150 | xs.lock.Lock() 151 | defer xs.lock.Unlock() 152 | 153 | var c int = 0 154 | for i,gaps := range xs.s { 155 | for _, b := range gaps { 156 | //判断坐标是否到头 157 | if xs.curWSlotPos == i && xs.curWGapPos == c{ 158 | return nil 159 | } 160 | c = c + 1 161 | 162 | v,err := xds.UnMarshal(xs._stype,b) 163 | if err != nil{ 164 | return nil 165 | } 166 | 167 | if err := f(c,v);err != nil{ 168 | return err 169 | } 170 | } 171 | } 172 | 173 | return nil 174 | } 175 | 176 | //返回xslice长度 177 | func (xs *Xslice) Len() int { 178 | xs.lock.Lock() 179 | defer xs.lock.Unlock() 180 | return xs._len 181 | } 182 | 183 | //@todo 把扩容逻辑封装起来 184 | func (xs *Xslice) increase() *Xslice { 185 | return nil 186 | } -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 XDS project Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // XDS Project Site: https://github.com/heiyeluren 16 | // XDS URL: https://github.com/heiyeluren/xds 17 | // 18 | 19 | package xds 20 | 21 | import ( 22 | "errors" 23 | "reflect" 24 | "unsafe" 25 | // "github.com/heiyeluren/xmm" 26 | ) 27 | 28 | // 定义XDS共用的常量和一些基础函数 29 | //调用方法: 30 | /* 31 | //包含 32 | import( 33 | "github.com/heiyeluren/xds" 34 | ) 35 | 36 | //结构体中使用 37 | type ConcurrentHashMap struct { 38 | keyKind xds.Kind 39 | valKind xds.Kind 40 | data *ConcurrentRawHashMap 41 | } 42 | 43 | //一般数据结构中使用 44 | m, err := xds.NewMap(mm, xds.String, xds.Int) 45 | 46 | //序列化处理 (其他类型到 []byte,两个函数效果一样) 47 | data, err := xds.Marshal(xds.String, "heiyeluren") 48 | data, err := xds.RawToByte(xds.String, "heiyeluren") 49 | 50 | // 反序列化处理 (从 []byte到原始类型) 51 | str, err := xds.UnMarshal(xds.String, data) 52 | data, err := xds.ByteToRaw(xds.String, "heiyeluren") 53 | 54 | */ 55 | 56 | 57 | 58 | //======================================================== 59 | // 60 | // XDS 常量和数据结构定义区 61 | // XDS constant and data structure definition 62 | // 63 | //======================================================== 64 | 65 | // XDS 中数据类型的定义 66 | // map[keyKind]valKind == xds.NewMap(mm, xds.keyKind, xds.valKind) 67 | // make([]typeKind, len) == xds.NewSlice(mm, xds.dataKind) 68 | 69 | type Kind uint 70 | 71 | const ( 72 | Invalid Kind = iota 73 | Bool 74 | Int 75 | Int8 76 | Int16 77 | Int32 78 | Int64 79 | Uint 80 | Uint8 81 | Uint16 82 | Uint32 83 | Uint64 84 | Uintptr 85 | Float32 86 | Float64 87 | Complex64 88 | Complex128 89 | Array 90 | Chan 91 | Func 92 | Interface 93 | Map 94 | Ptr 95 | ByteSlice 96 | String 97 | Struct 98 | UnsafePointer 99 | ) 100 | 101 | // 类型错误 102 | // type error 103 | var InvalidType = errors.New("Kind type Error") 104 | 105 | // type ConcurrentHashMap struct { 106 | // keyKind Kind 107 | // valKind Kind 108 | // data *ConcurrentRawHashMap 109 | // } 110 | 111 | 112 | 113 | //======================================================== 114 | // 115 | // XDS 常用函数定义区 116 | // XDS common function definition area 117 | // 118 | //======================================================== 119 | 120 | // 针对一些Kind数据类型的序列化 121 | // Serialization for some kind data types 122 | func Marshal(kind Kind, content interface{}) (data []byte, err error) { 123 | switch kind { 124 | case String: 125 | data, ok := content.(string) 126 | if !ok { 127 | return nil, InvalidType 128 | } 129 | sh := (*reflect.StringHeader)(unsafe.Pointer(&data)) 130 | return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: sh.Data, Len: sh.Len, Cap: sh.Len})), nil 131 | case ByteSlice: 132 | data, ok := content.([]byte) 133 | if !ok { 134 | return nil, InvalidType 135 | } 136 | return data, nil 137 | case Int: 138 | h, ok := content.(int) 139 | if !ok { 140 | return nil, InvalidType 141 | } 142 | return (*[8]byte)(unsafe.Pointer(&h))[:], nil 143 | case Uintptr: 144 | h, ok := content.(uintptr) 145 | if !ok { 146 | return nil, InvalidType 147 | } 148 | return (*[8]byte)(unsafe.Pointer(&h))[:], nil 149 | } 150 | return 151 | } 152 | 153 | // 针对一些Kind数据类型的反序列化 154 | // Deserialization for some kind data types 155 | func UnMarshal(kind Kind, data []byte) (content interface{}, err error) { 156 | switch kind { 157 | case String: 158 | return *(*string)(unsafe.Pointer(&data)), nil 159 | case Uintptr: 160 | sh := (*reflect.SliceHeader)(unsafe.Pointer(&data)) 161 | return *(*uintptr)(unsafe.Pointer(sh.Data)), nil 162 | case Int: 163 | sh := (*reflect.SliceHeader)(unsafe.Pointer(&data)) 164 | return *(*int)(unsafe.Pointer(sh.Data)), nil 165 | case ByteSlice: 166 | return data, nil 167 | } 168 | return 169 | } 170 | 171 | // Marshal 函数的别名 172 | // Marshal function link name 173 | func RawToByte(kind Kind, content interface{}) (data []byte, err error) { 174 | return Marshal(kind, content) 175 | } 176 | 177 | // UnMarshal 函数的别名 178 | // UnMarshal function link name 179 | func ByteToRaw(kind Kind, data []byte) (content interface{}, err error) { 180 | return UnMarshal(kind, data) 181 | } 182 | 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | GoDoc 4 | GoDoc 5 | Go Report Card 6 |

7 | 8 | 9 | ## XDS - eXtensible Data Structure Sets
(第三方可扩展的 Golang 高性能数据结构和数据类型合集) 10 | 11 | A third-party extensible collection of high-performance data structures and data types in Go 12 | 13 | - [XDS - eXtensible Data Structure
(第三方可扩展的 Go 语言中高性能数据结构和数据类型合集)](#xds---extensible-data-structure-第三方可扩展的-go-语言中高性能数据结构和数据类型合集) 14 | - [XDS 介绍:(什么是 Xds)](#xds-介绍什么是-xds) 15 | - [XDS - XMap 概要介绍](#xds---xmap-概要介绍) 16 | - [为什么要设计 XMap?](#为什么要设计-xmap) 17 | - [XMap 设计目标是什么?](#xmap-设计目标是什么) 18 | - [XMap 的技术特点](#xmap-的技术特点) 19 | - [XMap 性能数据和实现对比](#xmap-性能数据和实现对比) 20 | - [XMap 与 Go 官方数据结构特点对比:(20% 写入,80% 读场景)](#xmap-与-go-官方数据结构特点对比20-写入80-读场景) 21 | - [如何使用 XMap?](#如何使用-xmap) 22 | - [XMap 各类 API 使用案例](#xmap-各类-api-使用案例) 23 | - [- XMap 使用示例](#--xmap-使用示例) 24 | - [XMap 内部是如何实现的?](#xmap-内部是如何实现的) 25 | - [XDS 项目开发者](#xds-项目开发者) 26 | - [XDS 技术交流](#xds-技术交流) 27 | 28 |
29 | 30 | ## XDS 介绍:(什么是 Xds) 31 | 32 | XDS - eXtensible Data Structure(第三方可扩展的 Go 语言中高性能数据结构和数据类型合集) 33 | 34 | XDS 主要是为了解决现有 Go 语言官方内置的各类数据结构性能在高并发场景中不尽如人意的情况而开发,核心主要是依赖于 [XMM](https://github.com/heiyeluren/xmm) 内存管理库基础之上开发,保证了高性能和内存可控。 35 | 36 | XDS 集合目前主要包含: 37 | - XMap - 高性能的类似 map/sync.map 的 Map 型数据结构类型(已开源) 38 | - XSlice - 高性能类似 slice 的数组型数据结构类型(开发中) 39 | - XChannel - 高性能的 channel 管道类型结构(调研中) 40 | - 更多... 41 | 42 |
43 | 44 |
45 | 46 |
47 | 48 | ## XDS - XMap 概要介绍 49 | 50 | XMap 是属于高性能开源 Go 数据结构 Xds 中的 map 数据结构类型的实现,主要是基于高性能内存管理库 [XMM](https://github.com/heiyeluren/xmm) 基础之上进行的开发,主要弥补了 Go 内置 map 的无法并发读写,并且总体读写性能比较差的问题而开发。 51 | 52 |
53 | 54 | ### 为什么要设计 XMap? 55 | 56 | 现有 Golang 中的 map 数据结构无法解决并发读写问题,Sync.map 并发性能偏差,针对这个情况,各种高性能底层服务需要一个高性能、大容量、高并发、无 GC 的 Map,所以开发实现 XMap。 57 | 针对我们需求调研了市场上主要的 hashmap 结构,不能满足我们性能和功能要求。 58 | 59 |
60 | 61 | ### XMap 设计目标是什么? 62 | 63 | 要求设计一个可以并发读写不会出现 panic,要求并发读写 200w+ OPS/s 的并发 map 结构。 64 | (写 20%,读 80% 场景;说明:go 自带 map 读写性能在 80w ops/s,大量并发读写下可能 panic;sync.map 写入性能在 100w OPS/s) 65 | 66 |
67 | 68 | ### XMap 的技术特点 69 | 70 | - 绝对高性能的 map 数据结构(map 的 3 倍,sync.map 的 2 倍并发性能) 71 | - 内部实现机制对比 Go 原生 map/sync.map 技术设计上要更细致,更考虑性能,使用包括开地址法,红黑树等等结构提升性能; 72 | - 为了节约内存,初始的值比较低,但是依赖于 XMM 高性能内存扩容方式,能够快速进行内存扩容,保证并发写入性能 73 | - 底层采用 XMM 内存管理,不会受到 Go 系统本身 GC 机制的卡顿影响,保证高性能; 74 | - 提供 API 更具备扩展性,在一些高性能场景提供更多调用定制设置,并且能够同时支持 map 类型操作和底层 hash 表类型操作,适用场景更多; 75 | - 其他特性 76 | 77 |
78 | 79 | ### XMap 性能数据和实现对比 80 | 81 | XMap 目前并发读写场景下性能可以达到 200 万 op/s,对比原生 map 单机性能 80 万 op/s,提升了 3 倍 +,对比 Go 官方扩展库 sync.Map 性能有 2 倍的提升。 82 | 83 |
84 | 85 | #### XMap 与 Go 官方数据结构特点对比:(20% 写入,80% 读场景) 86 | 87 | | map 模块 | 性能数据
| 加锁机制 | 底层数据结构 | 内存机制 | 88 | |------|------|------|------|------| 89 | |map | 80w+ read/s
并发读写会 panic | 无 | Hashtable + Array | Go gc | 90 | |sync.Map | 100w+ op/s | RWMutex | map | Go gc | 91 | | Xds.XMap | 200w+ op/s | CAS + RWMutex | Hashtable + Array + RBTree | XMM | 92 | 93 |
94 |
95 | 96 | ## 如何使用 XMap? 97 | 98 | 快速使用: 99 | 100 | 1. 下载对应包 101 | 102 | ```shell 103 | go get -u github.com/heiyeluren/xds 104 | go get -u github.com/heiyeluren/xmm 105 | ``` 106 | 107 | 2. 快速包含调用库: 108 | 109 | ```go 110 | //注意:本代码只是伪代码,大家最好看这个使用测试案例更充分一些 111 | //详细使用案例:https://github.com/heiyeluren/xds/blob/main/example/xmap_test0.go 112 | //快速使用入门:https://github.com/heiyeluren/xds/blob/main/example/xmap_test1.go 113 | 114 | import ( 115 | xmm "github.com/heiyeluren/xmm" 116 | xds "github.com/heiyeluren/xds" 117 | xmap "github.com/heiyeluren/xds/xmap" 118 | ) 119 | 120 | // 创建一个 XMM 内存块 121 | f := &xmm.Factory{} 122 | mm, err := f.CreateMemory(0.75) 123 | 124 | // 构建一个 map[string]string 的 xmap 125 | m, err := xmap.NewMap(mm, xds.String, xds.String) 126 | 127 | // 写入、读取、删除一个元素 128 | err = m.Set("name", "heiyeluren") 129 | ret, key_exists, err := m.Get("name") 130 | err = m.Remove("name") 131 | 132 | // 遍历整个map 133 | m.Each(func(key, val interface{}) error { 134 | fmt.Printf("For each XMap all key:[%s] value:[%s] \n", key, val) 135 | return nil 136 | }) 137 | 138 | //... 139 | 140 | ``` 141 | 142 | 3. 执行对应代码 143 | 144 | ```shell 145 | go run map-test.go 146 | ``` 147 | 148 |
149 | 150 | ### XMap 各类 API 使用案例 151 | 152 | #### - [Xmap 快速使用入门](https://github.com/heiyeluren/xds/blob/main/example/xmap_test1.go) 153 | 154 | #### - [XMap 详细使用示例](https://github.com/heiyeluren/xds/blob/main/example/xmap_test0.go) - 155 | 156 | - 更多案例(期待) 157 | 158 | 以上代码案例执行输出: 159 |
160 | 161 | 162 |
163 | 164 | ## XMap 内部是如何实现的? 165 | 166 | #### - [《Xds-XMap技术设计与实现》](https://github.com/heiyeluren/xds/blob/main/docs/Xmap-Implement.md) - 167 | 168 |
169 | 170 | - 参考:[《Go map 内部实现机制一》](https://www.jianshu.com/p/aa0d4808cbb8) | [《Go map 内部实现二》](https://zhuanlan.zhihu.com/p/406751292) | [《Golang sync.Map 性能及原理分析》](https://blog.csdn.net/u010853261/article/details/103848666) 171 | - 其他 172 | 173 |
174 | 175 |
176 | 177 |
178 |
179 |
180 | 181 | ## XDS 项目开发者 182 | 183 | | 项目角色 | 项目成员 | 184 | | ----------- | ----------- | 185 | | 项目发起人/负责人 | 黑夜路人 ( @heiyeluren )
老张 ( @Zhang-Jun-tao ) | 186 | | 项目开发者 | 老张 ( @Zhang-Jun-tao )
黑夜路人 ( @heiyeluren )
Viktor ( @guojun1992 ) | 187 | 188 |

189 | 190 | ## XDS 技术交流 191 | 192 | XDS 还在早期,当然也少不了一些问题和 bug,欢迎大家一起共创,或者直接提交 PR 等等。 193 | 194 | 195 | 欢迎加入 XDS 技术交流微信群,要加群,可以先关注添加如下公众号:
196 | (如无法看到图片,请直接微信里搜索公众号“黑夜路人技术”,关注发送“加群”字样信息即可 ) 197 | 198 | 199 | 200 | 201 | 202 |
203 |
204 | -------------------------------------------------------------------------------- /xmap/concurrent_hash_map_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 XDS project Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // XDS Project Site: https://github.com/heiyeluren 16 | // XDS URL: https://github.com/heiyeluren/xds 17 | // 18 | 19 | package xmap 20 | 21 | import ( 22 | "fmt" 23 | "math/rand" 24 | "reflect" 25 | "strconv" 26 | "sync" 27 | "testing" 28 | "time" 29 | "unsafe" 30 | // "xds/xmap/entry" 31 | // "github.com/spf13/cast" 32 | "github.com/heiyeluren/xmm" 33 | "github.com/heiyeluren/xds/xmap/entry" 34 | ) 35 | 36 | // 1000000000 37 | func BenchmarkCHM_Put(b *testing.B) { 38 | f := &xmm.Factory{} 39 | mm, err := f.CreateMemory(0.75) 40 | if err != nil { 41 | b.Fatal(err) 42 | } 43 | chm, err := NewConcurrentRawHashMap(mm, 16, 2, 8) 44 | if err != nil { 45 | b.Fatal(err) 46 | } 47 | keys := make([]string, 10000000) 48 | for i := 0; i < 10000000; i++ { 49 | keys[i] = strconv.Itoa(rand.Int()) 50 | } 51 | length := len(keys) 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | key := []byte(keys[rand.Int()%length]) 55 | if err := chm.Put(key, key); err != nil { 56 | b.Error(err) 57 | } 58 | } 59 | } 60 | 61 | func BenchmarkCHM_Get(b *testing.B) { 62 | f := &xmm.Factory{} 63 | mm, err := f.CreateMemory(0.75) 64 | if err != nil { 65 | b.Fatal(err) 66 | } 67 | chm, err := NewConcurrentRawHashMap(mm, 16, 2, 8) 68 | if err != nil { 69 | b.Fatal(err) 70 | } 71 | keys := make([]string, 8000000) 72 | for i := 0; i < 8000000; i++ { 73 | keys[i] = strconv.Itoa(rand.Int()) 74 | } 75 | length := len(keys) 76 | for _, key := range keys { 77 | 78 | if err := chm.Put([]byte(key), []byte(key)); err != nil { 79 | b.Error(err) 80 | } 81 | } 82 | b.ResetTimer() 83 | for i := 0; i < b.N; i++ { 84 | key := keys[rand.Int()%length] 85 | if _, exist, err := chm.Get([]byte(key)); err != nil || !exist { 86 | b.Error(err) 87 | } 88 | } 89 | } 90 | 91 | func Test_CHM_Concurrent_Get(t *testing.T) { 92 | // Init() 93 | f := &xmm.Factory{} 94 | mm, err := f.CreateMemory(0.75) 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | chm, err := NewConcurrentRawHashMap(mm, 16, 2, 8) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | keys := make([]string, 8000000) 103 | for i := 0; i < 8000000; i++ { 104 | keys[i] = strconv.Itoa(rand.Int() % 800000) 105 | } 106 | for _, key := range keys { 107 | if err := chm.Put([]byte(key), []byte(key)); err != nil { 108 | t.Error(err) 109 | } 110 | } 111 | var wait sync.WaitGroup 112 | wait.Add(10) 113 | fmt.Println("开始") 114 | t1 := time.Now() 115 | for j := 0; j < 10; j++ { 116 | go func(z int) { 117 | defer wait.Done() 118 | start, end := z*800000, (z+1)*800000 119 | for _, s := range keys[start:end] { 120 | if err := chm.Put([]byte(s), []byte(s)); err != nil { 121 | t.Error(err) 122 | } 123 | } 124 | }(j) 125 | } 126 | wait.Wait() 127 | fmt.Println(len(keys), time.Now().Sub(t1), len(keys)) 128 | <-time.After(time.Minute) 129 | } 130 | 131 | func TestGoCreateEntry(t *testing.T) { 132 | var wait sync.WaitGroup 133 | wait.Add(10) 134 | node := &entry.NodeEntry{} 135 | // nodes := make([]*entry.NodeEntry, 8000000) 136 | tt := time.Now() 137 | for j := 0; j < 10; j++ { 138 | go func(z int) { 139 | defer wait.Done() 140 | for i := 0; i < 800000; i++ { 141 | node.Key = []byte("keyPtr") 142 | node.Value = []byte("valPtr") 143 | node.Hash = 12121 144 | // nodes[z*800000+i] = node 145 | } 146 | }(j) 147 | } 148 | wait.Wait() 149 | fmt.Println(time.Now().Sub(tt)) 150 | } 151 | 152 | func TestCreateEntry(t *testing.T) { 153 | f := &xmm.Factory{} 154 | mm, err := f.CreateMemory(0.75) 155 | if err != nil { 156 | t.Fatal(err) 157 | } 158 | entryPtr, err := mm.Alloc(_NodeEntrySize) 159 | if err != nil { 160 | t.Fatal(err) 161 | } 162 | /* 80445440 \ 283598848 \ 228630528 \ 267530240 \97157120 \ 129908736 163 | keyPtr, valPtr, err := mm.From2("hjjhj", "jjjshjfhsdf") 164 | if err != nil { 165 | t.Fatal(err) 166 | }*/ 167 | pageNum := float64(uintptr(entryPtr)) / 4096.0 168 | fmt.Println(uintptr(entryPtr), pageNum, (pageNum+1)*4096 > float64(uintptr(entryPtr)+_NodeEntrySize)) 169 | node := (*entry.NodeEntry)(entryPtr) 170 | tt := time.Now() 171 | var wait sync.WaitGroup 172 | wait.Add(10) 173 | for j := 0; j < 10; j++ { 174 | go func(z int) { 175 | defer wait.Done() 176 | for i := 0; i < 8000000; i++ { 177 | node.Key = []byte("keyPtr") 178 | node.Value = []byte("valPtr") 179 | node.Hash = 12121 180 | } 181 | }(j) 182 | } 183 | wait.Wait() 184 | fmt.Println(time.Now().Sub(tt)) 185 | } 186 | 187 | // todo 优秀一些,使用这种方式 188 | func TestFieldCopy(t *testing.T) { 189 | f := &xmm.Factory{} 190 | mm, err := f.CreateMemory(0.75) 191 | if err != nil { 192 | t.Fatal(err) 193 | } 194 | 195 | var wait sync.WaitGroup 196 | wait.Add(10) 197 | for j := 0; j < 10; j++ { 198 | go func(z int) { 199 | defer wait.Done() 200 | for i := 0; i < 800000; i++ { 201 | entryPtr, err := mm.Alloc(_NodeEntrySize) 202 | if err != nil { 203 | t.Fatal(err) 204 | } 205 | keyPtr, valPtr, err := mm.Copy2([]byte("hjjhj"), []byte("jjjshjfhsdf")) 206 | if err != nil { 207 | t.Fatal(err) 208 | } 209 | source := entry.NodeEntry{Value: keyPtr, Key: valPtr, Hash: 12312} 210 | offset := unsafe.Offsetof(source.Next) // 40 211 | srcData := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&source)), Len: int(offset), Cap: int(offset)})) 212 | dstData := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(entryPtr), Len: int(offset), Cap: int(offset)})) 213 | copy(*dstData, *srcData) 214 | } 215 | }(j) 216 | } 217 | wait.Wait() 218 | } 219 | -------------------------------------------------------------------------------- /example/xmap_test0.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cast" 6 | "github.com/heiyeluren/xmm" 7 | "github.com/heiyeluren/xds" 8 | "github.com/heiyeluren/xds/xmap" 9 | ) 10 | 11 | // TestMap testing 12 | // ----------------------------------- 13 | // 把Xmap当做普通map来使用 14 | // 说明:类型不限制,初始化必须设定好数据类型,写入数据必须与这个数据类型一致,类似于 map[KeyType]ValType 必须相互符合 15 | // ----------------------------------- 16 | /* 17 | 目前支持的类似于 map[keyType][valType] key value 类型如下: 18 | 就是调用:m, err := xds.NewMap(mm, xmap.String, xmap.Int) 后面的两个 keyType 和 valType 类型定义如下: 19 | const ( 20 | Invalid Kind = iota 21 | Bool 22 | Int 23 | Int8 24 | Int16 25 | Int32 26 | Int64 27 | Uint 28 | Uint8 29 | Uint16 30 | Uint32 31 | Uint64 32 | Uintptr 33 | Float32 34 | Float64 35 | Complex64 36 | Complex128 37 | Array 38 | Chan 39 | Func 40 | Interface 41 | Map 42 | Ptr 43 | ByteSlice 44 | String 45 | Struct 46 | UnsafePointer 47 | ) 48 | */ 49 | func TestMap(mm xmm.XMemory) { 50 | 51 | // ------------------- 52 | // 常规的map操作 53 | // ------------------- 54 | 55 | fmt.Println("\n--[ XMap NewMap() API example]--") 56 | // 初始化xmap的时候必须制定key和val的数据类型,数据类型是在xmap中定义的 57 | // 构建一个 map[string]int 的xmap 58 | m, err := xmap.NewMap(mm, xds.String, xds.Int) 59 | if err != nil { 60 | panic("call NewMap() fail") 61 | } 62 | 63 | var ( 64 | k11 string = "id" 65 | v11 int = 9527 66 | ) 67 | 68 | // set的时候需要关注类型是否跟初始化对象的时候一致 69 | err = m.Set(k11, v11) 70 | if err != nil { 71 | panic("XMap.Set fail") 72 | } 73 | fmt.Println("XMap.Set key: [", k11, "] success") 74 | 75 | // get数据不用关心类型,也不用做类型转换 76 | ret, exists, err := m.Get(k11) 77 | if err != nil { 78 | panic("XMap.Get fail") 79 | } 80 | fmt.Println("XMap.Get key: [", k11, "] , value: [", ret, "]") 81 | 82 | // Remove数据 83 | err = m.Remove(k11) 84 | if err != nil { 85 | panic("XMap.Remove fail") 86 | } 87 | fmt.Println("XMap.Remove key: [", k11, "] succes") 88 | ret, exists, err = m.Get(k11) 89 | if !exists { 90 | fmt.Println("XMap.Get key: [", k11, "] not found") 91 | } 92 | 93 | 94 | // ------------------- 95 | // 调用扩展的Map函数使用方法(可以获得更多定制性和更高性能) 96 | // ------------------- 97 | 98 | fmt.Println("\n--[ XMap NewMapEx() API example]--") 99 | 100 | 101 | // 生成KV数据 102 | var ( 103 | k22 = "name" 104 | v22 = "heiyeluren" 105 | ) 106 | // 生成一个 map[string]string 数据结构,默认大小256个元素,占用了75%后进行map扩容(这个初始化函数可以获得更好性能,看个人使用场景) 107 | m1, err := xmap.NewMapEx(mm, xds.String, xds.String, uintptr(256), 0.75) 108 | // set数据 109 | m1.Set(k22, v22) 110 | // get数据 111 | ret, exists, err = m1.Get(k22) 112 | fmt.Println("XMap.Get key: [", k22, "] , value: [", ret, "]") 113 | 114 | 115 | 116 | // ------------------- 117 | // 遍历所有map数据 118 | // ------------------- 119 | 120 | fmt.Println("\n--[ XMap ForEach all Key ]--") 121 | 122 | // 写入数据 123 | err = m.Set("k1", 1) 124 | err = m.Set("k2", 2) 125 | err = m.Set("k3", 3) 126 | err = m.Set("k4", 4) 127 | err = m.Set("k5", 5) 128 | 129 | //全局变量可以在匿名函数中访问(如果需要使用外部变量,可以像这样) 130 | gi := 1 131 | fmt.Printf("for each itam start, gi: [%s] \n", gi) 132 | 133 | // 遍历xmap中所有元素 134 | m.Each(func(key, val interface{}) error { 135 | //针对每个KV进行操作,比如打印出来 136 | fmt.Printf("for each XMap all key:[%s] value:[%s] \n", key, val) 137 | //外部变量使用操作 138 | gi++ 139 | return nil 140 | }) 141 | fmt.Printf("for each itam done, gi: [%s] \n", gi) 142 | 143 | //读取map长度 144 | len := m.Len() 145 | fmt.Printf("\nMap length(size): [%s] \n", len) 146 | 147 | } 148 | 149 | 150 | // TestHashMap testing 151 | // ----------------------------------- 152 | // 把Xmap当做普通hashmap来使用 153 | // 说明:Key/Value 都必须是 []byte 154 | // ----------------------------------- 155 | func TestHashMap(mm xmm.XMemory) { 156 | 157 | fmt.Println("\n\n===== XMap X(eXtensible) Raw Map (HashMap) example ======\n") 158 | 159 | hm, err := xmap.NewHashMap(mm) 160 | if err != nil { 161 | panic("call NewHashMap() fail") 162 | } 163 | 164 | var ( 165 | k1 string = "name" 166 | v1 string = "heiyeluren" 167 | k2 string = "id" 168 | v2 uint32 = 9527 169 | ) 170 | 171 | // 新增Key 172 | fmt.Println("\n--[ Raw Map Set Key ]--") 173 | err = hm.Set([]byte(k1), []byte(v1)) 174 | if err != nil { 175 | panic("xmap.Set fail") 176 | } 177 | fmt.Println("Xmap.Set key: [", k1, "] success") 178 | err = hm.Set([]byte(k2), []byte(cast.ToString(v2))) 179 | if err != nil { 180 | panic("xmap.Set fail") 181 | } 182 | fmt.Println("Xmap.Set key: [", k2, "] success") 183 | 184 | // 读取Key 185 | fmt.Println("\n--[ Raw Map Get Key ]--") 186 | s1, exists, err := hm.Get([]byte(k1)) 187 | if err != nil { 188 | panic("xmap.Get fail") 189 | } 190 | fmt.Println("Xmap.Get key: [", k1, "], value: [", cast.ToString(s1), "]") 191 | s2, exists, err := hm.Get([]byte(k2)) 192 | if err != nil { 193 | panic("xmap.Get fail") 194 | } 195 | fmt.Println("Xmap.Get key: [", k2, "], value: [", cast.ToString(s2), "]") 196 | 197 | // 删除Key 198 | fmt.Println("\n--[ Raw Map Remove Key ]--") 199 | err = hm.Remove([]byte(k1)) 200 | if err != nil { 201 | panic("xmap.Remove fail") 202 | } 203 | fmt.Println("Xmap.Remove key: [", k1, "]") 204 | s1, exists, err = hm.Get([]byte(k1)) 205 | // fmt.Println(s1, exists, err) 206 | if !exists { 207 | fmt.Println("Xmap.Get key: [", k1, "] Not Found") 208 | } 209 | s2, exists, err = hm.Get([]byte(k2)) 210 | if err != nil { 211 | panic("xmap.Get fail") 212 | } 213 | fmt.Println("Xmap.Get key: [", k2, "], value: [", cast.ToString(s2), "]") 214 | err = hm.Remove([]byte(k2)) 215 | if err != nil { 216 | panic("xmap.Remove fail") 217 | } 218 | fmt.Println("Xmap.Remove key: [", k2, "]") 219 | s2, exists, err = hm.Get([]byte(k2)) 220 | // fmt.Println(s1, exists, err) 221 | if !exists { 222 | fmt.Println("Xmap.Get key: [", k2, "] Not Found") 223 | } 224 | s1, exists, err = hm.Get([]byte(k1)) 225 | // fmt.Println(s1, exists, err) 226 | if !exists { 227 | fmt.Println("Xmap.Get key: [", k1, "] Not Found") 228 | } 229 | 230 | //-------------------- 231 | // 遍历RawMap 232 | //-------------------- 233 | fmt.Println("\n--[ Raw Map for each all Key ]--") 234 | hm1, err := xmap.NewHashMap(mm) 235 | 236 | // 写入数据 237 | hm1.Set([]byte("K1"), []byte("V1")) 238 | hm1.Set([]byte("K2"), []byte("V2")) 239 | hm1.Set([]byte("K3"), []byte("V3")) 240 | hm1.Set([]byte("K4"), []byte("V4")) 241 | 242 | //hm1.Each(func(key, val []byte)(error) { 243 | // fmt.Println("for each raw map key:[", key, "], val[", val, "]") 244 | //}) 245 | 246 | 247 | //全局变量可以在匿名函数中访问(如果需要使用外部变量,可以像这样) 248 | gi := 1 249 | fmt.Printf("for each itam start, gi: [%s] \n", gi) 250 | 251 | // 遍历xmap中所有元素 252 | hm1.Each(func(key, val []byte) error { 253 | //针对每个KV进行操作,比如打印出来 254 | fmt.Printf("for each XMap all key:[%s] value:[%s] \n", key, val) 255 | //外部变量使用操作 256 | gi++ 257 | return nil 258 | }) 259 | //读取map长度 260 | len := hm1.Len() 261 | fmt.Printf("\nMap length(size): [%s] \n", len) 262 | 263 | } 264 | 265 | // xmap测试代码 266 | func main() { 267 | f := &xmm.Factory{} 268 | mm, err := f.CreateMemory(0.75) 269 | if err != nil { 270 | panic("xmm.CreateConcurrentHashMapMemory fail") 271 | } 272 | fmt.Println("\n===== XMap X(eXtensible) Map example ======\n") 273 | 274 | // var NotFound = errors.New("not found") 275 | 276 | // 把Xmap当做普通map来使用 277 | TestMap(mm) 278 | 279 | // 把Xmap当做普通hashmap来使用 280 | TestHashMap(mm) 281 | 282 | fmt.Println("\nXmap test case done.\n\n") 283 | 284 | } 285 | -------------------------------------------------------------------------------- /xmap/xmap.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 XDS project Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // XDS Project Site: https://github.com/heiyeluren 16 | // XDS URL: https://github.com/heiyeluren/xds 17 | // 18 | 19 | package xmap 20 | 21 | import ( 22 | "github.com/heiyeluren/xds" 23 | // "xds" 24 | "github.com/heiyeluren/xmm" 25 | ) 26 | 27 | // XMap is a map of maps. 28 | // ------------------------------------------------ 29 | // 当做map[]来使用的场景 30 | // 本套API主要是提供给把Xmap当做map来使用的场景 31 | // ------------------------------------------------ 32 | 33 | // Xmap struct 34 | type XMap struct { 35 | chm *ConcurrentHashMap 36 | } 37 | 38 | // NewMap returns a new XMap. 39 | // 初始化调用xmap生成对象 40 | // mm 是XMM的内存池对象 41 | // keyKind 是要生成 map[keyType]valType 中的 keyType 42 | // valKind 是要生成 map[keyType]valType 中的 valType 43 | // 说明:keyKind / valKind 都是直接调用xmap中对应的kind类型,必须提前初始化写入 44 | // 45 | // 本函数调用参考: 46 | // 生成一个 map[int]string 数据结构,默认大小16个元素,占用了75%后进行map扩容 47 | // m, err := xds.NewMapEx(mm, xmap.Int, xmap.String) 48 | func NewMap(mm xmm.XMemory, keyKind xds.Kind, valKind xds.Kind) (*XMap, error) { 49 | chm, err := NewDefaultConcurrentHashMap(mm, keyKind, valKind) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return &XMap{chm: chm}, nil 54 | } 55 | 56 | // NewMapEx returns a new XMap. 57 | // 初始化调用xmap生成对象 - 可扩展方法 58 | // mm 是XMM的内存池对象 59 | // keyKind 是要生成 map[keyType]valType 中的 keyType 60 | // valKind 是要生成 map[keyType]valType 中的 valType 61 | // 说明:keyKind / valKind 都是直接调用xmap中对应的kind类型,必须提前初始化写入 62 | // capSize 是默认初始化整个map的大小,本值最好是 2^N 的数字比较合适(2的N次方); 63 | // 这个值主要提升性能,比如如果你预估到最终会有128个元素,可以初始化时候设置好,这样Xmap不会随意的动态扩容(默认值是16),提升性能 64 | // factSize 负载因子,当存放的元素超过该百分比,就会触发扩容;建议值是0.75 (75%),这就是当存储数据容量达到75%会触发扩容机制; 65 | // 66 | // 本函数调用参考: 67 | // //生成一个 map[int]string 数据结构,默认大小256个元素,占用了75%后进行map扩容 68 | // m, err := xds.NewMapEx(mm, xmap.Int, xmap.String, (uintptr)256, 0.75) 69 | func NewMapEx(mm xmm.XMemory, keyKind xds.Kind, valKind xds.Kind, capSize uintptr, factSize float64) (*XMap, error) { 70 | chm, err := NewConcurrentHashMap(mm, capSize, factSize, 8, keyKind, valKind) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return &XMap{chm: chm}, nil 75 | } 76 | 77 | // Set 写入数据 78 | func (xm *XMap) Set(key interface{}, val interface{}) (err error) { 79 | return xm.chm.Put(key, val) 80 | } 81 | 82 | // Remove 删除数据 83 | func (xm *XMap) Remove(key interface{}) (err error) { 84 | return xm.chm.Del(key) 85 | } 86 | 87 | // Get 数据 88 | func (xm *XMap) Get(key interface{}) (val interface{}, keyExists bool, err error) { 89 | return xm.chm.Get(key) 90 | } 91 | 92 | // Each 遍历所有数据 93 | func (xm *XMap) Each(f func(key, val interface{}) error) error { 94 | return xm.chm.ForEach(f) 95 | } 96 | 97 | // Len 获取整个map的元素数量 98 | func (xm *XMap) Len() uint64 { 99 | return xm.chm.Len() 100 | } 101 | 102 | 103 | 104 | // RawMap - Hashmap 105 | // ------------------------------------------------ 106 | // 当做原生Hash Map来使用场景 107 | // 本套API主要是提供给把Xmap当做Hash表来使用的场景 108 | // ------------------------------------------------ 109 | // 定义xmap的入口主结构 110 | type RawMap struct { 111 | chm *ConcurrentRawHashMap 112 | } 113 | 114 | // NewHashMap returns a new RawMap. 115 | // 初始化调用xmap - hashmap 生成对象 mm 是XMM的内存池对象 116 | func NewHashMap(mm xmm.XMemory) (*RawMap, error) { 117 | chm, err := NewDefaultConcurrentRawHashMap(mm) 118 | if err != nil { 119 | return nil, err 120 | } 121 | return &RawMap{chm: chm}, nil 122 | } 123 | 124 | // Set 写入数据 125 | func (xm *RawMap) Set(key []byte, val []byte) error { 126 | return xm.chm.Put(key, val) 127 | } 128 | 129 | // Remove 删除数据 130 | func (xm *RawMap) Remove(key []byte) error { 131 | return xm.chm.Del(key) 132 | } 133 | 134 | // Get 数据 135 | func (xm *RawMap) Get(key []byte) ([]byte, bool, error) { 136 | return xm.chm.Get(key) 137 | } 138 | 139 | // Each 遍历所有数据 140 | func (xm *RawMap) Each(f func(key, val []byte) error) error { 141 | return xm.chm.ForEach(f) 142 | } 143 | 144 | // Len 获取整个map的元素数量 145 | func (xm *RawMap) Len() uint64 { 146 | return xm.chm.size 147 | } 148 | 149 | 150 | 151 | 152 | 153 | // The data structure and interface between xMAP and the bottom layer 154 | // ------------------------------------------------ 155 | // xmap与底层交互的数据结构和接口 156 | // 157 | // 主要提供给上层xmap api调用,完成一些转换工作 158 | // ------------------------------------------------ 159 | 160 | //底层交互数据结构(带有传入数据类型保存) 161 | type ConcurrentHashMap struct { 162 | keyKind xds.Kind 163 | valKind xds.Kind 164 | data *ConcurrentRawHashMap 165 | } 166 | 167 | // NewDefaultConcurrentHashMap 类似于make(map[keyKind]valKind) 168 | // mm 内存分配模块 169 | // keyKind: map中key的类型 170 | // valKind: map中value的类型 171 | func NewDefaultConcurrentHashMap(mm xmm.XMemory, keyKind, valKind xds.Kind) (*ConcurrentHashMap, error) { 172 | return NewConcurrentHashMap(mm, 16, 0.75, 8, keyKind, valKind) 173 | } 174 | 175 | // NewConcurrentHashMap 类似于make(map[keyKind]valKind) 176 | // mm 内存分配模块 177 | // keyKind: map中key的类型 178 | // cap:初始化bucket长度 179 | // fact:负载因子,当存放的元素超过该百分比,就会触发扩容。 180 | // treeSize:bucket中的链表长度达到该值后,会转换为红黑树。 181 | // valKind: map中value的类型 182 | func NewConcurrentHashMap(mm xmm.XMemory, cap uintptr, fact float64, treeSize uint64, keyKind, valKind xds.Kind) (*ConcurrentHashMap, error) { 183 | chm, err := NewConcurrentRawHashMap(mm, cap, fact, treeSize) 184 | if err != nil { 185 | return nil, err 186 | } 187 | return &ConcurrentHashMap{keyKind: keyKind, valKind: valKind, data: chm}, nil 188 | } 189 | 190 | //Get 191 | func (chm *ConcurrentHashMap) Get(key interface{}) (val interface{}, keyExists bool, err error) { 192 | // k, err := chm.Marshal(chm.keyKind, key) 193 | k, err := xds.RawToByte(chm.keyKind, key) 194 | if err != nil { 195 | return nil, false, err 196 | } 197 | valBytes, exists, err := chm.data.Get(k) 198 | if err != nil { 199 | return nil, exists, err 200 | } 201 | // ret, err := chm.UnMarshal(chm.valKind, valBytes) 202 | ret, err := xds.ByteToRaw(chm.valKind, valBytes) 203 | if err != nil { 204 | return nil, false, err 205 | } 206 | return ret, true, nil 207 | } 208 | 209 | //Put 210 | func (chm *ConcurrentHashMap) Put(key interface{}, val interface{}) (err error) { 211 | // k, err := chm.Marshal(chm.keyKind, key) 212 | k, err := xds.RawToByte(chm.keyKind, key) 213 | if err != nil { 214 | return err 215 | } 216 | // v, err := chm.Marshal(chm.valKind, val) 217 | v, err := xds.RawToByte(chm.valKind, val) 218 | return chm.data.Put(k, v) 219 | } 220 | 221 | //Del 222 | func (chm *ConcurrentHashMap) Del(key interface{}) (err error) { 223 | // k, err := chm.Marshal(chm.keyKind, key) 224 | k, err := xds.RawToByte(chm.keyKind, key) 225 | if err != nil { 226 | return err 227 | } 228 | return chm.data.Del(k) 229 | } 230 | 231 | // ForEach 遍历所有key-vel对 232 | func (chm *ConcurrentHashMap) ForEach(fun func(key, val interface{}) error) error { 233 | return chm.data.ForEach(func(keyRaw, valRaw []byte) error { 234 | key, err := xds.ByteToRaw(chm.keyKind, keyRaw) 235 | if err != nil { 236 | return err 237 | } 238 | val, err := xds.ByteToRaw(chm.valKind, valRaw) 239 | if err != nil { 240 | return err 241 | } 242 | return fun(key, val) 243 | }) 244 | } 245 | 246 | // Len 存储元素个数 247 | func (chm *ConcurrentHashMap) Len() uint64 { 248 | if chm.data == nil { 249 | return 0 250 | } 251 | return chm.data.size 252 | } 253 | -------------------------------------------------------------------------------- /xmap/concurrent_hash_map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 XDS project Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // XDS Project Site: https://github.com/heiyeluren 16 | // XDS URL: https://github.com/heiyeluren/xds 17 | // 18 | 19 | package xmap 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | "io/ioutil" 25 | "math/rand" 26 | "os" 27 | "path/filepath" 28 | "strings" 29 | "sync" 30 | "testing" 31 | "time" 32 | "unsafe" 33 | // "xds/xmap/entry" 34 | "github.com/heiyeluren/xds/xmap/entry" 35 | "github.com/heiyeluren/xmm" 36 | "github.com/spf13/cast" 37 | ) 38 | 39 | func TestMap(t *testing.T) { 40 | t1 := time.Now() 41 | var data sync.Map 42 | var wait sync.WaitGroup 43 | wait.Add(10) 44 | for h := 0; h < 10; h++ { 45 | go func() { 46 | defer wait.Done() 47 | for i := 0; i < 1000000; i++ { 48 | key := cast.ToString(i) 49 | data.Store(key, key) 50 | } 51 | }() 52 | } 53 | wait.Wait() 54 | fmt.Println(time.Now().Sub(t1)) 55 | } 56 | 57 | func TestConcurrentRawHashMap_Performance(t *testing.T) { 58 | Init() 59 | f := &xmm.Factory{} 60 | mm, err := f.CreateMemory(0.6) 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | chm, err := NewConcurrentRawHashMap(mm, 16, 0.75, 8) 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | fmt.Println("http://localhost:6060/debug/pprof/profile") 69 | // http://localhost:6060/debug/pprof/profile 70 | for i := 0; i < 1000000000; i++ { 71 | key := cast.ToString(i) 72 | if err := chm.Put([]byte(key), []byte(key)); err != nil { 73 | t.Error(err) 74 | } 75 | } 76 | } 77 | 78 | func TestConcurrentRawHashMap_Function_Second(t *testing.T) { 79 | f := &xmm.Factory{} 80 | mm, err := f.CreateMemory(0.75) 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | chm, err := NewConcurrentRawHashMap(mm, 16, 4, 8) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | var wait sync.WaitGroup 89 | wait.Add(10) 90 | keys := make(chan string, 1000000) 91 | quit := make(chan bool, 1) 92 | go func() { 93 | <-time.After(time.Second * 22) 94 | t2 := time.Now() 95 | for { 96 | key, ok := <-keys 97 | if !ok { 98 | fmt.Println("read time:", time.Now().Sub(t2)) 99 | quit <- true 100 | return 101 | } 102 | val, exist, err := chm.Get([]byte(cast.ToString(key))) 103 | if bytes.Compare(val, []byte(cast.ToString(key))) != 0 || !exist { 104 | t.Error(err, key) 105 | } 106 | } 107 | }() 108 | t1 := time.Now() 109 | for j := 0; j < 10; j++ { 110 | go func(z int) { 111 | defer wait.Done() 112 | for i := 0; i < 100000; i++ { 113 | key := cast.ToString(i + (z * 10000)) 114 | if err := chm.Put([]byte(key), []byte(key)); err != nil { 115 | t.Error(err) 116 | } else { 117 | keys <- key 118 | } 119 | } 120 | }(j) 121 | } 122 | wait.Wait() 123 | fmt.Println(len(keys), time.Now().Sub(t1), len(keys)) 124 | close(keys) 125 | f.PrintStatus() 126 | 127 | <-quit 128 | <-time.After(time.Second * 10) 129 | } 130 | 131 | func TestConcurrentRawHashMap_Function1(t *testing.T) { 132 | Init() 133 | // runtime.GOMAXPROCS(16) 134 | f := &xmm.Factory{} 135 | mm, err := f.CreateMemory(0.75) 136 | if err != nil { 137 | t.Fatal(err) 138 | } 139 | chm, err := NewConcurrentRawHashMap(mm, 16, 4, 8) 140 | if err != nil { 141 | t.Fatal(err) 142 | } 143 | var wait sync.WaitGroup 144 | wait.Add(10) 145 | keys := make(chan string, 8000000) 146 | quit := make(chan bool, 1) 147 | go func() { 148 | <-time.After(time.Second * 10) 149 | t2 := time.Now() 150 | for { 151 | key, ok := <-keys 152 | if !ok { 153 | fmt.Println("read time:", time.Now().Sub(t2)) 154 | quit <- true 155 | return 156 | } 157 | val, exist, err := chm.Get([]byte(key)) 158 | if bytes.Compare(val, []byte(key)) != 0 || !exist { 159 | t.Fatal(err, key) 160 | } 161 | } 162 | }() 163 | t1 := time.Now() 164 | for j := 0; j < 10; j++ { 165 | go func(z int) { 166 | defer wait.Done() 167 | for i := 0; i < 800000; i++ { 168 | key := cast.ToString(i + (z * 10000)) 169 | if err := chm.Put([]byte(key), []byte(key)); err != nil { 170 | t.Error(err) 171 | } else { 172 | keys <- key 173 | } 174 | } 175 | }(j) 176 | } 177 | wait.Wait() 178 | fmt.Println(len(keys), time.Now().Sub(t1), len(keys)) 179 | close(keys) 180 | <-quit 181 | } 182 | 183 | func TestMMM(t *testing.T) { 184 | // /usr/local/go/src/runtime 185 | filepath.Walk("/usr/local/go/src/runtime", func(path string, info os.FileInfo, err error) error { 186 | if info.IsDir() { 187 | return nil 188 | } 189 | if path[len(path)-3:] != ".go" { 190 | return nil 191 | } 192 | bytes, err := ioutil.ReadFile(path) 193 | if err != nil { 194 | return err 195 | } 196 | for _, s1 := range strings.Split(string(bytes), "\n") { 197 | s1 = strings.TrimSpace(s1) 198 | if len(s1) < 3 || s1[:len("//")] == "//" || strings.Contains(s1, " = ") || strings.Contains(info.Name(), "_test.go") { 199 | continue 200 | } 201 | for _, s2 := range strings.Split(s1, " ") { 202 | s := strings.TrimSpace(s2) 203 | if len(s) < 1 { 204 | continue 205 | } 206 | if int(s[0]) <= 64 || int(s[0]) >= 91 { 207 | continue 208 | } 209 | fmt.Println( /*path, info.Name(),*/ s, s1) 210 | } 211 | } 212 | return nil 213 | }) 214 | } 215 | 216 | type A struct { 217 | lock sync.RWMutex 218 | age int 219 | } 220 | 221 | func TestSizeOf(t *testing.T) { 222 | fmt.Println(unsafe.Sizeof(sync.RWMutex{}), unsafe.Sizeof(A{})) 223 | fmt.Println(unsafe.Sizeof(Bucket2{})) 224 | } 225 | 226 | type Bucket2 struct { 227 | forwarding bool // 已经迁移完成 228 | rwlock sync.RWMutex 229 | index uint64 230 | newBuckets *[]uintptr 231 | Head *entry.NodeEntry 232 | /* 233 | Tree *entry.Tree 234 | isTree bool 235 | size uint64 236 | */ 237 | } 238 | 239 | func TestMMCopyString(t *testing.T) { 240 | f := &xmm.Factory{} 241 | mm, err := f.CreateMemory(0.75) 242 | if err != nil { 243 | t.Fatal(err) 244 | } 245 | var item1, item2 string 246 | for i := 0; i < 10000000; i++ { 247 | item1, item2, err = mm.From2("sdsddsds", "sdsddsdssdsds") 248 | if err != nil { 249 | t.Error(err) 250 | } 251 | } 252 | fmt.Println(item1, item2) 253 | } 254 | 255 | func TestGoCopyString(t *testing.T) { 256 | f := &xmm.Factory{} 257 | mm, err := f.CreateMemory(0.75) 258 | if err != nil { 259 | t.Fatal(err) 260 | } 261 | var item1, item2 string 262 | for i := 0; i < 10000000; i++ { 263 | item1, item2, err = mm.From2("sdsddsds", "sdsddsdssdsds") 264 | if err != nil { 265 | t.Error(err) 266 | } 267 | } 268 | fmt.Println(item1, item2) 269 | } 270 | 271 | type KV struct { 272 | K string 273 | V string 274 | } 275 | 276 | func TestDataSet(t *testing.T) { 277 | fmt.Println(string(make([]byte, 100))) 278 | num, maxLen := 10000000, 1000 279 | kvs := make([]*KV, num) 280 | for i := 0; i < num; i++ { 281 | r := rand.New(rand.NewSource(time.Now().UnixNano())).Int() 282 | k := RandString(r % maxLen) 283 | kvs[i] = &KV{ 284 | K: cast.ToString(r % num), 285 | V: k, 286 | } 287 | } 288 | fmt.Println(RandString(1000)) 289 | } 290 | 291 | func RandString(len int) string { 292 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 293 | bytes := make([]byte, len) 294 | for i := 0; i < len; i++ { 295 | b := r.Intn(26) + 65 296 | bytes[i] = byte(b) 297 | } 298 | return string(bytes) 299 | } 300 | 301 | /*func TestDecode(t *testing.T) { 302 | node := entry.NodeEntry{} 303 | sss := node.Int64Encode(212121) 304 | fmt.Println(node.Int64Decode(sss)) 305 | 306 | }*/ 307 | -------------------------------------------------------------------------------- /xmap/map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 XDS project Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // XDS Project Site: https://github.com/heiyeluren 16 | // XDS URL: https://github.com/heiyeluren/xds 17 | // 18 | 19 | package xmap 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | "github.com/heiyeluren/xds" 25 | "github.com/heiyeluren/xds/xmap/entry" 26 | "github.com/heiyeluren/xmm" 27 | "log" 28 | "math/rand" 29 | "net/http" 30 | _ "net/http/pprof" 31 | "os" 32 | "reflect" 33 | "runtime" 34 | "strconv" 35 | "strings" 36 | "sync" 37 | "sync/atomic" 38 | "testing" 39 | "time" 40 | "unsafe" 41 | // "xds/xmap/entry" 42 | "github.com/spf13/cast" 43 | ) 44 | 45 | type User struct { 46 | Name int 47 | Age int 48 | Addr string 49 | } 50 | 51 | // mudi : 1001 -> 10000 52 | // 1001 << 1 -> 10010 53 | // 10010 & (1001 & 0) 54 | func round(n uintptr) uintptr { 55 | return (n << 1) & (0 & (n)) 56 | } 57 | 58 | type mmanClass uint8 59 | 60 | func makemmanClass(sizeclass uint8, noscan bool) mmanClass { 61 | return mmanClass(sizeclass<<1) | mmanClass(bool2int(noscan)) 62 | } 63 | 64 | func (sc mmanClass) sizeclass() int8 { 65 | return int8(sc >> 1) 66 | } 67 | 68 | func (sc mmanClass) noscan() bool { 69 | return sc&1 != 0 70 | } 71 | 72 | func bool2int(x bool) int { 73 | // Avoid branches. In the SSA compiler, this compiles to 74 | // exactly what you would want it to. 75 | return int(uint8(*(*uint8)(unsafe.Pointer(&x)))) 76 | } 77 | 78 | func Init() { 79 | // 略 80 | runtime.GOMAXPROCS(6) // 限制 CPU 使用数,避免过载 81 | runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪 82 | runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪 83 | 84 | go func() { 85 | // 启动一个 http server,注意 pprof 相关的 handler 已经自动注册过了 86 | if err := http.ListenAndServe(":6060", nil); err != nil { 87 | log.Fatal(err) 88 | } 89 | os.Exit(0) 90 | }() 91 | <-time.After(time.Second * 10) 92 | } 93 | 94 | func Test_xmmanPool222(t *testing.T) { 95 | // 同步扩容 680 异步扩容 551 96 | fmt.Println(100000 / (4096 / 48)) 97 | f := &xmm.Factory{} 98 | 99 | mm, err := f.CreateMemory(0.6) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | var s unsafe.Pointer 104 | sl, err := mm.AllocSlice(unsafe.Sizeof(s), 100000+1, 0) 105 | if err != nil { 106 | t.Fatal(err) 107 | } 108 | ssss := *(*[]unsafe.Pointer)(sl) 109 | var start unsafe.Pointer 110 | data := (*reflect.SliceHeader)(unsafe.Pointer(&ssss)).Data 111 | var us []unsafe.Pointer 112 | for i := 0; i < 100000; i++ { 113 | p, err := mm.Alloc(unsafe.Sizeof(User{})) 114 | if err != nil { 115 | t.Fatal(err) 116 | } 117 | user := (*User)(p) 118 | user.Age = i 119 | user.Name = rand.Int() 120 | us = append(us, p) 121 | ssss = append(ssss, p) 122 | if start == nil { 123 | start = ssss[0] 124 | } 125 | } 126 | if (*reflect.SliceHeader)(unsafe.Pointer(&ssss)).Data != data { 127 | t.Fatal("扩容了") 128 | } 129 | fmt.Printf("sssssss %d %d\n ", start, ssss[0]) 130 | if ssss[0] != start { 131 | t.Fatal("-") 132 | } 133 | for i, pointer := range us { 134 | tep := (*User)(ssss[i]) 135 | if sss := (*User)(pointer); sss.Age != i || tep.Age != sss.Age { 136 | t.Fatalf("%+v\n", pointer) 137 | } 138 | } 139 | } 140 | 141 | func TestPointer2(t *testing.T) { 142 | tmp := make([]*User, 10000000) 143 | us := &tmp 144 | var wait sync.WaitGroup 145 | wait.Add(80) 146 | var sm sync.Map 147 | for j := 0; j < 80; j++ { 148 | go func(z int) { 149 | defer wait.Done() 150 | for i := 0; i < 10000000; i++ { 151 | key := cast.ToString(i + (z * 1000)) 152 | addr := (*unsafe.Pointer)(unsafe.Pointer(&(*us)[i])) 153 | user := atomic.LoadPointer(addr) 154 | if user == nil { 155 | ut := &User{ 156 | Name: i, 157 | Age: i, 158 | Addr: key, 159 | } 160 | ptr := unsafe.Pointer(ut) 161 | if atomic.CompareAndSwapPointer(addr, nil, ptr) { 162 | sm.Store(i, uintptr(ptr)) 163 | } 164 | } 165 | } 166 | }(j) 167 | } 168 | wait.Wait() 169 | 170 | for i := 0; i < 10000000; i++ { 171 | addr := (*unsafe.Pointer)(unsafe.Pointer(&(*us)[i])) 172 | user := atomic.LoadPointer(addr) 173 | u := (*User)(user) 174 | if val, ok := sm.Load(i); !ok || val != uintptr(unsafe.Pointer(u)) { 175 | t.Fatal(val, user, i, u) 176 | } 177 | } 178 | } 179 | 180 | func TestPointer(t *testing.T) { 181 | tmp := make([]uintptr, 1000000) 182 | var users []*User 183 | us := &tmp 184 | var wait sync.WaitGroup 185 | wait.Add(80) 186 | var sm sync.Map 187 | for j := 0; j < 80; j++ { 188 | go func(z int) { 189 | defer wait.Done() 190 | for i := 0; i < 100000; i++ { 191 | key := cast.ToString(i + (z * 1000)) 192 | addr := &((*us)[i]) 193 | user := atomic.LoadUintptr(addr) 194 | if user == 0 { 195 | ut := &User{ 196 | Name: i, 197 | Age: i, 198 | Addr: key, 199 | } 200 | ptr := uintptr(unsafe.Pointer(ut)) 201 | if atomic.CompareAndSwapUintptr(addr, 0, ptr) { 202 | users = append(users, ut) 203 | sm.Store(i, ptr) 204 | // fmt.Printf("i:%d ptr:%d\n", i, ptr) 205 | } 206 | } 207 | } 208 | }(j) 209 | } 210 | wait.Wait() 211 | 212 | for i := 0; i < 100000; i++ { 213 | addr := &((*us)[i]) 214 | user := atomic.LoadUintptr(addr) 215 | u := (*User)(unsafe.Pointer(user)) 216 | if val, ok := sm.Load(i); !ok || val != user { 217 | t.Fatal(val, user, i, u) 218 | } 219 | } 220 | } 221 | 222 | // todo CompareAndSwapPointer xuexi 223 | 224 | func TestRBTree(t *testing.T) { 225 | rbt := new(entry.Tree) 226 | rbt.SetComparator(func(o1, o2 interface{}) int { 227 | key1, key2 := o1.(string), o2.(string) 228 | return strings.Compare(key1, key2) 229 | }) 230 | num := 10000000 231 | st := time.Now() 232 | for i := 0; i < num/10; i++ { 233 | key := cast.ToString(i) 234 | ce := &entry.NodeEntry{ 235 | Value: []byte(key), 236 | Key: []byte(key), 237 | Hash: uint64(rand.Int()), 238 | } 239 | rbt.Put(ce) 240 | } 241 | for i := 0; i < num/10; i++ { 242 | exist, node := rbt.Get([]byte(strconv.Itoa(i))) 243 | if !exist || bytes.Compare(node, []byte(strconv.Itoa(i))) != 0 { 244 | panic(i) 245 | } 246 | } 247 | fmt.Println(rbt.Get([]byte(strconv.Itoa(5)))) 248 | fmt.Println(time.Now().Sub(st).Seconds()) 249 | rbt.Walk(&entry.HookVisitor{Hook: func(node *entry.NodeEntry) { 250 | fmt.Println(node) 251 | }}) 252 | } 253 | 254 | func Test_NewDefaultConcurrentHashMap(t *testing.T) { 255 | f := &xmm.Factory{} 256 | mm, err := f.CreateMemory(0.6) 257 | if err != nil { 258 | t.Fatal(err) 259 | } 260 | chmp, err := NewDefaultConcurrentHashMap(mm, xds.Uintptr, xds.Uintptr) 261 | if err != nil { 262 | t.Fatal(err) 263 | } 264 | for i := 0; i < 10000000; i++ { 265 | s := uintptr(i) 266 | if err := chmp.Put(s, s); err != nil { 267 | t.Error(err) 268 | } 269 | } 270 | for i := 0; i < 10000000; i++ { 271 | s := uintptr(i) 272 | if val, exist, err := chmp.Get(s); err != nil || val != s || !exist { 273 | t.Error("sss", err) 274 | } 275 | } 276 | } 277 | 278 | func TestXmap_ForEach(t *testing.T) { 279 | f := &xmm.Factory{} 280 | mm, err := f.CreateMemory(0.6) 281 | if err != nil { 282 | t.Fatal(err) 283 | } 284 | chmp, err := NewDefaultConcurrentHashMap(mm, xds.Uintptr, xds.Uintptr) 285 | if err != nil { 286 | t.Fatal(err) 287 | } 288 | for i := 0; i < 10000; i++ { 289 | s := uintptr(i) 290 | if err := chmp.Put(s, s); err != nil { 291 | t.Error(err) 292 | } 293 | } 294 | for i := 0; i < 10000; i++ { 295 | s := uintptr(i) 296 | if val, exist, err := chmp.Get(s); err != nil || val != s || !exist { 297 | t.Error("sss", err) 298 | } 299 | } 300 | i := 0 301 | err = chmp.ForEach(func(key, val interface{}) error { 302 | //fmt.Printf("ForEach key:%s value:%s \n", key, val) 303 | i++ 304 | return nil 305 | }) 306 | fmt.Println(i, chmp.Len()) 307 | if err != nil { 308 | t.Error("sss", err) 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /xmap/entry/rbtree.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Gavin Bong. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, 11 | software distributed under the License is distributed on an 12 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 13 | either express or implied. See the License for the specific 14 | language governing permissions and limitations under the 15 | License. 16 | */ 17 | 18 | // Package entry Package redblacktree provides a pure Golang implementation 19 | // of a red-black tree as described by Thomas H. Cormen's et al. 20 | // in their seminal Algorithms book (3rd ed). This data structure 21 | // is not multi-goroutine safe. 22 | package entry 23 | 24 | import ( 25 | "bytes" 26 | "errors" 27 | "fmt" 28 | "io" 29 | "io/ioutil" 30 | "log" 31 | "os" 32 | "reflect" 33 | "strings" 34 | "sync" 35 | ) 36 | 37 | // NodeEntry interface defines the methods that a node must implement. 38 | type NodeEntry struct { 39 | Key []byte 40 | Value []byte 41 | Hash uint64 42 | Next *NodeEntry 43 | 44 | right *NodeEntry 45 | left *NodeEntry 46 | parent *NodeEntry 47 | color Color // 比二叉查找树要多出一个颜色属性 48 | } 49 | 50 | func round(n, a uint64) uint64 { 51 | return (n + a - 1) &^ (a - 1) 52 | } 53 | 54 | type encodedNodeEntry struct { 55 | TotalLen uint64 56 | KeyLen uint64 57 | ValueLen uint64 58 | Hash uint64 59 | Next *NodeEntry 60 | 61 | right *NodeEntry 62 | left *NodeEntry 63 | parent *NodeEntry 64 | color Color // 比二叉查找树要多出一个颜色属性 65 | 66 | // 追加string key、value 要不然就内存泄露了 67 | 68 | } 69 | 70 | /*// total'len(64) + key'len(64) + key'content + 71 | func (n *NodeEntry) encode() []byte { 72 | var keyLen, valLen = round(uint64(len(n.Key)), 8), round(uint64(len(n.Value)), 8) 73 | total := keyLen + valLen + 8*8 74 | encoded := encodedNodeEntry{ 75 | TotalLen: total, 76 | KeyLen: keyLen, 77 | ValueLen: valLen, 78 | Hash: 0, 79 | Next: nil, 80 | right: nil, 81 | left: nil, 82 | parent: nil, 83 | color: false, 84 | } 85 | 86 | }*/ 87 | 88 | // BytesAscSort is a helper function that sorts a slice of byte slices 89 | var BytesAscSort Comparator = func(o1, o2 interface{}) int { 90 | key1, key2 := o1.([]byte), o2.([]byte) 91 | return bytes.Compare(key1, key2) 92 | } 93 | 94 | // Color of a redblack tree node is either 95 | // `Black` (true) & `Red` (false) 96 | type Color bool 97 | 98 | // Direction points to either the Left or Right subtree 99 | type Direction byte 100 | 101 | func (c Color) String() string { 102 | switch c { 103 | case true: 104 | return "Black" 105 | default: 106 | return "Red" 107 | } 108 | } 109 | 110 | func (d Direction) String() string { 111 | switch d { 112 | case LEFT: 113 | return "left" 114 | case RIGHT: 115 | return "right" 116 | case NODIR: 117 | return "center" 118 | default: 119 | return "not recognized" 120 | } 121 | } 122 | 123 | const ( 124 | BLACK, RED Color = true, false 125 | ) 126 | const ( 127 | LEFT Direction = iota 128 | RIGHT 129 | NODIR 130 | ) 131 | 132 | // A node needs to be able to answer the query: 133 | // (i) Who is my parent node ? 134 | // (ii) Who is my grandparent node ? 135 | // The zero value for Node has color Red. 136 | 137 | func (n *NodeEntry) String() string { 138 | if n == nil { 139 | return "" 140 | } 141 | return fmt.Sprintf("(%#v : %s)", n.Key, n.Color()) 142 | } 143 | 144 | // Parent node will be nil if the node is the root node. 145 | func (n *NodeEntry) Parent() *NodeEntry { 146 | return n.parent 147 | } 148 | 149 | // SetColor nodes color to the given color. 150 | func (n *NodeEntry) SetColor(color Color) { 151 | n.color = color 152 | } 153 | 154 | // Color returns the color of the node. 155 | func (n *NodeEntry) Color() Color { 156 | return n.color 157 | } 158 | 159 | // Left indicates the left child of the node. 160 | func (n *NodeEntry) Left() *NodeEntry { 161 | return n.left 162 | } 163 | 164 | // Right indicates the right child of the node. 165 | func (n *NodeEntry) Right() *NodeEntry { 166 | return n.right 167 | } 168 | 169 | // Visitor is a function that is called on each node of the tree. 170 | type Visitor interface { 171 | Visit(*NodeEntry) 172 | } 173 | 174 | // Visitable A redblack tree is `Visitable` by a `Visitor`. 175 | type Visitable interface { 176 | Walk(Visitor) 177 | } 178 | 179 | // Comparator is a function that compares two objects. 180 | // Keys must be comparable. It's mandatory to provide a Comparator, 181 | // which returns zero if o1 == o2, -1 if o1 < o2, 1 if o1 > o2 182 | type Comparator func(o1, o2 interface{}) int 183 | 184 | // IntComparator is a Comparator that compares ints. 185 | // Default comparator expects keys to be of type `int`. 186 | // Warning: if either one of `o1` or `o2` cannot be asserted to `int`, it panics. 187 | func IntComparator(o1, o2 interface{}) int { 188 | i1 := o1.(int) 189 | i2 := o2.(int) 190 | switch { 191 | case i1 > i2: 192 | return 1 193 | case i1 < i2: 194 | return -1 195 | default: 196 | return 0 197 | } 198 | } 199 | 200 | // StringComparator ... 201 | // Keys of type `string`. 202 | // Warning: if either one of `o1` or `o2` cannot be asserted to `string`, it panics. 203 | func StringComparator(o1, o2 interface{}) int { 204 | s1 := o1.(string) 205 | s2 := o2.(string) 206 | return bytes.Compare([]byte(s1), []byte(s2)) 207 | } 208 | 209 | // Tree encapsulates the data structure. 210 | type Tree struct { 211 | root *NodeEntry // tip of the tree 212 | cmp Comparator // required function to order keys 213 | lock sync.RWMutex 214 | } 215 | 216 | // `lock` protects `logger` 217 | var loggerlock sync.Mutex 218 | var logger *log.Logger 219 | 220 | func init() { 221 | logger = log.New(ioutil.Discard, "", log.LstdFlags) 222 | } 223 | 224 | // TraceOn turns on logging output to Stderr 225 | func TraceOn() { 226 | SetOutput(os.Stderr) 227 | } 228 | 229 | // TraceOff turns off logging. 230 | // By default logging is turned off. 231 | func TraceOff() { 232 | SetOutput(ioutil.Discard) 233 | } 234 | 235 | // SetOutput redirects log output 236 | func SetOutput(w io.Writer) { 237 | loggerlock.Lock() 238 | defer loggerlock.Unlock() 239 | logger = log.New(w, "", log.LstdFlags) 240 | } 241 | 242 | // NewTree returns an empty Tree with default comparator `IntComparator`. 243 | // `IntComparator` expects keys to be type-assertable to `int`. 244 | func NewTree() *Tree { 245 | return &Tree{root: nil, cmp: IntComparator} 246 | } 247 | 248 | // NewTreeWith returns an empty Tree with a supplied `Comparator`. 249 | func NewTreeWith(c Comparator) *Tree { 250 | return &Tree{root: nil, cmp: c} 251 | } 252 | 253 | // SetComparator in the Tree of the supplied `Comparator`. 254 | func (t *Tree) SetComparator(c Comparator) { 255 | t.cmp = c 256 | } 257 | 258 | // Get looks for the node with supplied key and returns its mapped payload. 259 | // Return value in 1st position indicates whether any payload was found. 260 | func (t *Tree) Get(key []byte) (bool, []byte) { 261 | if err := mustBeValidKey(key); err != nil { 262 | // logger.Printf("Get was prematurely aborted: %s\n", err.Error()) 263 | return false, nil 264 | } 265 | ok, node := t.getNode(key) 266 | if ok { 267 | return true, node.Value 268 | } 269 | return false, nil 270 | } 271 | 272 | func (t *Tree) getNode(key interface{}) (bool, *NodeEntry) { 273 | found, parent, dir := t.GetParent(key) 274 | if found { 275 | if parent == nil { 276 | return true, t.root 277 | } 278 | var node *NodeEntry 279 | switch dir { 280 | case LEFT: 281 | node = parent.left 282 | case RIGHT: 283 | node = parent.right 284 | } 285 | 286 | if node != nil { 287 | return true, node 288 | } 289 | } 290 | return false, nil 291 | } 292 | 293 | // getMinimum returns the node with minimum key starting 294 | // at the subtree rooted at node x. Assume x is not nil. 295 | func (t *Tree) getMinimum(x *NodeEntry) *NodeEntry { 296 | for { 297 | if x.left != nil { 298 | x = x.left 299 | } else { 300 | return x 301 | } 302 | } 303 | } 304 | 305 | // GetParent looks for the node with supplied key and returns the parent node. 306 | func (t *Tree) GetParent(key interface{}) (found bool, parent *NodeEntry, dir Direction) { 307 | if err := mustBeValidKey(key); err != nil { 308 | // logger.Printf("GetParent was prematurely aborted: %s\n", err.Error()) 309 | return false, nil, NODIR 310 | } 311 | 312 | if t.root == nil { 313 | return false, nil, NODIR 314 | } 315 | t.lock.RLock() 316 | defer t.lock.RUnlock() 317 | return t.internalLookup(nil, t.root, key, NODIR) 318 | } 319 | 320 | func (t *Tree) internalLookup(parent *NodeEntry, this *NodeEntry, key interface{}, dir Direction) (bool, *NodeEntry, Direction) { 321 | switch { 322 | case this == nil: 323 | return false, parent, dir 324 | case t.cmp(key, this.Key) == 0: 325 | return true, parent, dir 326 | case t.cmp(key, this.Key) < 0: 327 | return t.internalLookup(this, this.left, key, LEFT) 328 | case t.cmp(key, this.Key) > 0: 329 | return t.internalLookup(this, this.right, key, RIGHT) 330 | default: 331 | return false, parent, NODIR 332 | } 333 | } 334 | 335 | // RotateRight Reverses actions of RotateLeft 336 | func (t *Tree) RotateRight(y *NodeEntry) { 337 | if y == nil { 338 | // logger.Printf("RotateRight: nil arg cannot be rotated. Noop\n") 339 | return 340 | } 341 | if y.left == nil { 342 | // logger.Printf("RotateRight: y has nil left subtree. Noop\n") 343 | return 344 | } 345 | // logger.Printf("\t\t\trotate right of %s\n", y) 346 | x := y.left 347 | y.left = x.right 348 | if x.right != nil { 349 | x.right.parent = y 350 | } 351 | x.parent = y.parent 352 | if y.parent == nil { 353 | t.root = x 354 | } else { 355 | if y == y.parent.left { 356 | y.parent.left = x 357 | } else { 358 | y.parent.right = x 359 | } 360 | } 361 | x.right = y 362 | y.parent = x 363 | } 364 | 365 | // RotateLeft Reverses actions of RotateRight 366 | // Side-effect: red-black tree properties is maintained. 367 | func (t *Tree) RotateLeft(x *NodeEntry) { 368 | if x == nil { 369 | // logger.Printf("RotateLeft: nil arg cannot be rotated. Noop\n") 370 | return 371 | } 372 | if x.right == nil { 373 | // logger.Printf("RotateLeft: x has nil right subtree. Noop\n") 374 | return 375 | } 376 | // logger.Printf("\t\t\trotate left of %s\n", x) 377 | 378 | y := x.right 379 | x.right = y.left 380 | if y.left != nil { 381 | y.left.parent = x 382 | } 383 | y.parent = x.parent 384 | if x.parent == nil { 385 | t.root = y 386 | } else { 387 | if x == x.parent.left { 388 | x.parent.left = y 389 | } else { 390 | x.parent.right = y 391 | } 392 | } 393 | y.left = x 394 | x.parent = y 395 | } 396 | 397 | // Put saves the mapping (key, data) into the tree. 398 | // If a mapping identified by `key` already exists, it is overwritten. 399 | // Constraint: Not everything can be a key. 400 | func (t *Tree) Put(node *NodeEntry) error { 401 | node.parent, node.left, node.Next, node.right = nil, nil, nil, nil 402 | key, data := node.Key, node.Value 403 | if err := mustBeValidKey(key); err != nil { 404 | // logger.Printf("Put was prematurely aborted: %s\n", err.Error()) 405 | return err 406 | } 407 | t.lock.Lock() 408 | defer t.lock.Unlock() 409 | if t.root == nil { 410 | node.color = BLACK 411 | t.root = node 412 | // logger.Printf("Added %s as root node\n", t.root.String()) 413 | return nil 414 | } 415 | 416 | found, parent, dir := t.internalLookup(nil, t.root, key, NODIR) 417 | if found { 418 | if parent == nil { 419 | // logger.Printf("Put: parent=nil & found. Overwrite ROOT node\n") 420 | t.root.Value = data 421 | } else { 422 | // logger.Printf("Put: parent!=nil & found. Overwriting\n") 423 | switch dir { 424 | case LEFT: 425 | parent.left.Value = data 426 | case RIGHT: 427 | parent.right.Value = data 428 | } 429 | } 430 | 431 | } else { 432 | if parent != nil { 433 | node.parent = parent 434 | newNode := node 435 | switch dir { 436 | case LEFT: 437 | parent.left = newNode 438 | case RIGHT: 439 | parent.right = newNode 440 | } 441 | // logger.Printf("Added %s to %s node of parent %s\n", newNode.String(), dir, parent.String()) 442 | t.fixupPut(newNode) 443 | } 444 | } 445 | return nil 446 | } 447 | 448 | func isRed(n *NodeEntry) bool { 449 | key := reflect.ValueOf(n) 450 | if key.IsNil() { 451 | return false 452 | } 453 | return n.color == RED 454 | } 455 | 456 | // fix possible violations of red-black-tree properties 457 | // with combinations of: 458 | // 1. recoloring 459 | // 2. rotations 460 | // 461 | // Preconditions: 462 | // P1) z is not nil 463 | // 464 | // @param z - the newly added Node to the tree. 465 | func (t *Tree) fixupPut(z *NodeEntry) { 466 | // logger.Printf("\tfixup new node z %s\n", z.String()) 467 | loop: 468 | for { 469 | // logger.Printf("\tcurrent z %s\n", z.String()) 470 | switch { 471 | case z.parent == nil: 472 | fallthrough 473 | case z.parent.color == BLACK: 474 | fallthrough 475 | default: 476 | // When the loop terminates, it does so because p[z] is black. 477 | // logger.Printf("\t\t=> bye\n") 478 | break loop 479 | case z.parent.color == RED: 480 | grandparent := z.parent.parent 481 | // logger.Printf("\t\tgrandparent is nil %t addr:%d\n", grandparent == nil, unsafe.Pointer(t)) 482 | if z.parent == grandparent.left { 483 | // logger.Printf("\t\t%s is the left child of %s\n", z.parent, grandparent) 484 | y := grandparent.right 485 | // //logger.Printf("\t\ty (right) %s\n", y) 486 | if isRed(y) { 487 | // case 1 - y is RED 488 | // logger.Printf("\t\t(*) case 1\n") 489 | z.parent.color = BLACK 490 | y.color = BLACK 491 | grandparent.color = RED 492 | z = grandparent 493 | 494 | } else { 495 | if z == z.parent.right { 496 | // case 2 497 | // logger.Printf("\t\t(*) case 2\n") 498 | z = z.parent 499 | t.RotateLeft(z) 500 | } 501 | 502 | // case 3 503 | // logger.Printf("\t\t(*) case 3\n") 504 | z.parent.color = BLACK 505 | grandparent.color = RED 506 | t.RotateRight(grandparent) 507 | } 508 | } else { 509 | // logger.Printf("\t\t%s is the right child of %s\n", z.parent, grandparent) 510 | y := grandparent.left 511 | // logger.Printf("\t\ty (left) %s\n", y) 512 | if isRed(y) { 513 | // case 1 - y is RED 514 | // logger.Printf("\t\t..(*) case 1\n") 515 | z.parent.color = BLACK 516 | y.color = BLACK 517 | grandparent.color = RED 518 | z = grandparent 519 | 520 | } else { 521 | // logger.Printf("\t\t## %s\n", z.parent.left) 522 | if z == z.parent.left { 523 | // case 2 524 | // logger.Printf("\t\t..(*) case 2\n") 525 | z = z.parent 526 | t.RotateRight(z) 527 | } 528 | 529 | // case 3 530 | // logger.Printf("\t\t..(*) case 3\n") 531 | z.parent.color = BLACK 532 | grandparent.color = RED 533 | t.RotateLeft(grandparent) 534 | } 535 | } 536 | } 537 | } 538 | t.root.color = BLACK 539 | } 540 | 541 | // Size returns the number of items in the tree. 542 | func (t *Tree) Size() uint64 { 543 | visitor := &countingVisitor{} 544 | t.Walk(visitor) 545 | return visitor.Count 546 | } 547 | 548 | // Has checks for existence of a item identified by supplied key. 549 | func (t *Tree) Has(key interface{}) bool { 550 | if err := mustBeValidKey(key); err != nil { 551 | // logger.Printf("Has was prematurely aborted: %s\n", err.Error()) 552 | return false 553 | } 554 | found, _, _ := t.internalLookup(nil, t.root, key, NODIR) 555 | return found 556 | } 557 | 558 | func (t *Tree) transplant(u *NodeEntry, v *NodeEntry) { 559 | if u.parent == nil { 560 | t.root = v 561 | } else if u == u.parent.left { 562 | u.parent.left = v 563 | } else { 564 | u.parent.right = v 565 | } 566 | if v != nil && u != nil { 567 | v.parent = u.parent 568 | } 569 | } 570 | 571 | // Delete removes the item identified by the supplied key. 572 | // Delete is a noop if the supplied key doesn't exist. 573 | func (t *Tree) Delete(key []byte) *NodeEntry { 574 | if !t.Has(key) { 575 | // logger.Printf("Delete: bail as no node exists for key %d\n", key) 576 | return nil 577 | } 578 | _, z := t.getNode(key) 579 | y := z 580 | yOriginalColor := y.color 581 | var x *NodeEntry 582 | 583 | if z.left == nil { 584 | // one child (RIGHT) 585 | // logger.Printf("\t\tDelete: case (a)\n") 586 | x = z.right 587 | // logger.Printf("\t\t\t--- x is right of z") 588 | t.transplant(z, z.right) 589 | 590 | } else if z.right == nil { 591 | // one child (LEFT) 592 | // logger.Printf("\t\tDelete: case (b)\n") 593 | x = z.left 594 | // logger.Printf("\t\t\t--- x is left of z") 595 | t.transplant(z, z.left) 596 | 597 | } else { 598 | // two children 599 | // logger.Printf("\t\tDelete: case (c) & (d)\n") 600 | y = t.getMinimum(z.right) 601 | // logger.Printf("\t\t\tminimum of z.right is %s (color=%s)\n", y, y.color) 602 | yOriginalColor = y.color 603 | x = y.right 604 | // logger.Printf("\t\t\t--- x is right of minimum") 605 | 606 | if y.parent == z { 607 | if x != nil { 608 | x.parent = y 609 | } 610 | } else { 611 | t.transplant(y, y.right) 612 | y.right = z.right 613 | y.right.parent = y 614 | } 615 | t.transplant(z, y) 616 | y.left = z.left 617 | y.left.parent = y 618 | y.color = z.color 619 | } 620 | if yOriginalColor == BLACK { 621 | t.fixupDelete(x) 622 | } 623 | return z 624 | } 625 | 626 | func (t *Tree) fixupDelete(x *NodeEntry) { 627 | // logger.Printf("\t\t\tfixupDelete of node %s\n", x) 628 | if x == nil { 629 | return 630 | } 631 | loop: 632 | for { 633 | switch { 634 | case x == t.root: 635 | // logger.Printf("\t\t\t=> bye .. is root\n") 636 | break loop 637 | case x.color == RED: 638 | // logger.Printf("\t\t\t=> bye .. RED\n") 639 | break loop 640 | case x == x.parent.right: 641 | // logger.Printf("\t\tBRANCH: x is right child of parent\n") 642 | w := x.parent.left // is nillable 643 | if isRed(w) { 644 | // Convert case 1 into case 2, 3, or 4 645 | // logger.Printf("\t\t\tR> case 1\n") 646 | w.color = BLACK 647 | x.parent.color = RED 648 | t.RotateRight(x.parent) 649 | w = x.parent.left 650 | } 651 | if w != nil { 652 | switch { 653 | case !isRed(w.left) && !isRed(w.right): 654 | // case 2 - both children of w are BLACK 655 | // logger.Printf("\t\t\tR> case 2\n") 656 | w.color = RED 657 | x = x.parent // recurse up tree 658 | case isRed(w.right) && !isRed(w.left): 659 | // case 3 - right child RED & left child BLACK 660 | // convert to case 4 661 | // logger.Printf("\t\t\tR> case 3\n") 662 | w.right.color = BLACK 663 | w.color = RED 664 | t.RotateLeft(w) 665 | w = x.parent.left 666 | } 667 | if isRed(w.left) { 668 | // case 4 - left child is RED 669 | // logger.Printf("\t\t\tR> case 4\n") 670 | w.color = x.parent.color 671 | x.parent.color = BLACK 672 | w.left.color = BLACK 673 | t.RotateRight(x.parent) 674 | x = t.root 675 | } 676 | } 677 | case x == x.parent.left: 678 | // logger.Printf("\t\tBRANCH: x is left child of parent\n") 679 | w := x.parent.right // is nillable 680 | if isRed(w) { 681 | // Convert case 1 into case 2, 3, or 4 682 | // logger.Printf("\t\t\tL> case 1\n") 683 | w.color = BLACK 684 | x.parent.color = RED 685 | t.RotateLeft(x.parent) 686 | w = x.parent.right 687 | } 688 | if w != nil { 689 | switch { 690 | case !isRed(w.left) && !isRed(w.right): 691 | // case 2 - both children of w are BLACK 692 | // logger.Printf("\t\t\tL> case 2\n") 693 | w.color = RED 694 | x = x.parent // recurse up tree 695 | case isRed(w.left) && !isRed(w.right): 696 | // case 3 - left child RED & right child BLACK 697 | // convert to case 4 698 | // logger.Printf("\t\t\tL> case 3\n") 699 | w.left.color = BLACK 700 | w.color = RED 701 | t.RotateRight(w) 702 | w = x.parent.right 703 | } 704 | if isRed(w.right) { 705 | // case 4 - right child is RED 706 | // logger.Printf("\t\t\tL> case 4\n") 707 | w.color = x.parent.color 708 | x.parent.color = BLACK 709 | w.right.color = BLACK 710 | t.RotateLeft(x.parent) 711 | x = t.root 712 | } 713 | } 714 | } 715 | } 716 | x.color = BLACK 717 | } 718 | 719 | // Walk accepts a Visitor 720 | func (t *Tree) Walk(visitor Visitor) { 721 | visitor.Visit(t.root) 722 | } 723 | 724 | // GetRoot get root node 725 | func (t *Tree) GetRoot() *NodeEntry { 726 | return t.root 727 | } 728 | 729 | // countingVisitor counts the number 730 | // of nodes in the tree. 731 | type countingVisitor struct { 732 | Count uint64 733 | } 734 | 735 | // Visit . 736 | func (v *countingVisitor) Visit(node *NodeEntry) { 737 | if node == nil { 738 | return 739 | } 740 | 741 | v.Visit(node.left) 742 | v.Count = v.Count + 1 743 | v.Visit(node.right) 744 | } 745 | 746 | // InorderVisitor walks the tree in inorder fashion. 747 | // This visitor maintains internal state; thus do not 748 | // reuse after the completion of a walk. 749 | type InorderVisitor struct { 750 | buffer bytes.Buffer 751 | } 752 | 753 | // Eq 比较 754 | func (v *InorderVisitor) Eq(other *InorderVisitor) bool { 755 | if other == nil { 756 | return false 757 | } 758 | return v.String() == other.String() 759 | } 760 | 761 | func (v *InorderVisitor) trim(s string) string { 762 | return strings.TrimRight(strings.TrimRight(s, "ed"), "lack") 763 | } 764 | 765 | // String returns string 766 | func (v *InorderVisitor) String() string { 767 | return v.buffer.String() 768 | } 769 | 770 | // Visit . 771 | func (v *InorderVisitor) Visit(node *NodeEntry) { 772 | if node == nil { 773 | v.buffer.Write([]byte(".")) 774 | return 775 | } 776 | v.buffer.Write([]byte("(")) 777 | v.Visit(node.left) 778 | v.buffer.Write([]byte(fmt.Sprintf("%s", node.Key))) // @TODO 779 | // v.buffer.Write([]byte(fmt.Sprintf("%d{%s}", node.Key, v.trim(node.color.String())))) 780 | v.Visit(node.right) 781 | v.buffer.Write([]byte(")")) 782 | } 783 | 784 | // HookVisitor . 785 | type HookVisitor struct { 786 | Hook func(node *NodeEntry) 787 | } 788 | 789 | // Visit . 790 | func (v *HookVisitor) Visit(node *NodeEntry) { 791 | if node == nil { 792 | return 793 | } 794 | v.Hook(node) 795 | v.Visit(node.left) 796 | v.Visit(node.right) 797 | } 798 | 799 | var ( 800 | // ErrorKeyIsNil the literal nil not allowed as keys 801 | ErrorKeyIsNil = errors.New("the literal nil not allowed as keys") 802 | // ErrorKeyDisallowed disallowed key type 803 | ErrorKeyDisallowed = errors.New("disallowed key type") 804 | ) 805 | 806 | // Allowed key types are: Boolean, Integer, Floating point, Complex, String values 807 | // And structs containing these. 808 | // @TODO Should pointer type be allowed ? 809 | func mustBeValidKey(key interface{}) error { 810 | if key == nil { 811 | return ErrorKeyIsNil 812 | } 813 | 814 | /*keyValue := reflect.ValueOf(key) 815 | switch keyValue.Kind() { 816 | case reflect.Chan: 817 | fallthrough 818 | case reflect.Func: 819 | fallthrough 820 | case reflect.Interface: 821 | fallthrough 822 | case reflect.Map: 823 | fallthrough 824 | case reflect.Ptr: 825 | return ErrorKeyDisallowed 826 | default: 827 | return nil 828 | }*/ 829 | return nil 830 | } 831 | -------------------------------------------------------------------------------- /xmap/concurrent_raw_hash_map.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 XDS project Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // XDS Project Site: https://github.com/heiyeluren 16 | // XDS URL: https://github.com/heiyeluren/xds 17 | // 18 | 19 | package xmap 20 | 21 | import ( 22 | "bytes" 23 | "errors" 24 | "github.com/heiyeluren/xds/xmap/entry" 25 | "github.com/heiyeluren/xmm" 26 | "log" 27 | "reflect" 28 | "runtime" 29 | "sync" 30 | "sync/atomic" 31 | "unsafe" 32 | ) 33 | 34 | // MinTransferStride is the minimum number of entries to transfer between 35 | // 1、步长resize 36 | // 2、sizeCtl增加cap的cas,不允许提前resize。 37 | // 考虑 数组+ 链表方式 38 | const MinTransferStride = 16 39 | const maximumCapacity = 1 << 30 40 | 41 | // KeyExists 标识key已经存在 42 | const KeyExists = true 43 | 44 | // KeyNotExists 标识key不存在 45 | const KeyNotExists = false 46 | 47 | var NotFound = errors.New("not found") 48 | var _BucketSize = unsafe.Sizeof(Bucket{}) 49 | var _ForwardingBucketSize = unsafe.Sizeof(ForwardingBucket{}) 50 | var _NodeEntrySize = unsafe.Sizeof(entry.NodeEntry{}) 51 | var _TreeSize = unsafe.Sizeof(entry.Tree{}) 52 | var uintPtrSize = uintptr(8) 53 | 54 | // ConcurrentRawHashMap is a concurrent hash map with a fixed number of buckets. 55 | // 清理方式:1、del清除entry(简单) 56 | // 2、buckets清除旧的(resize结束,并无get读引用【引入重试来解决该问题】) 57 | // 3、Bucket清除 58 | // 4、ForwardingBucket清除 59 | // 5、Tree 清除(spliceEntry2时候) 60 | type ConcurrentRawHashMap struct { 61 | size uint64 62 | threshold uint64 63 | initCap uint64 64 | // off-heap 65 | buckets *[]uintptr 66 | mm xmm.XMemory 67 | lock sync.RWMutex 68 | 69 | treeSize uint64 70 | 71 | // resize 72 | sizeCtl int64 // -1 正在扩容 73 | reSizeGen uint64 74 | 75 | nextBuckets *[]uintptr 76 | transferIndex uint64 77 | 78 | destroyed uint32 // 0:未销毁 1:已经销毁 79 | 80 | destroyLock sync.RWMutex 81 | } 82 | 83 | // Bucket is a hash bucket. 84 | type Bucket struct { 85 | forwarding bool // 已经迁移完成 86 | rwLock sync.RWMutex 87 | index uint64 88 | newBuckets *[]uintptr 89 | Head *entry.NodeEntry 90 | Tree *entry.Tree 91 | isTree bool 92 | size uint64 93 | } 94 | 95 | // ForwardingBucket is a hash bucket that has been forwarded to a new table. 96 | type ForwardingBucket struct { 97 | forwarding bool // 已经迁移完成 98 | rwLock sync.RWMutex 99 | index uint64 100 | newBuckets *[]uintptr 101 | } 102 | 103 | // Snapshot 利用快照比对产生 104 | type Snapshot struct { 105 | buckets *[]uintptr 106 | sizeCtl int64 // -1 正在扩容 107 | nextBuckets *[]uintptr 108 | } 109 | 110 | // NewDefaultConcurrentRawHashMap returns a new ConcurrentRawHashMap with the default 111 | // mm: xmm 112 | func NewDefaultConcurrentRawHashMap(mm xmm.XMemory) (*ConcurrentRawHashMap, error) { 113 | return NewConcurrentRawHashMap(mm, 16, 0.75, 8) 114 | } 115 | 116 | // NewConcurrentRawHashMap will create a new ConcurrentRawHashMap with the given 117 | // mm: xmm 内存对象 118 | // cap:初始化bucket长度 (可以理解为 map 元素预计最大个数~,如果知道这个值可以提前传递) 119 | // fact:负责因子,当存放的元素超过该百分比,就会触发扩容。 120 | // treeSize:bucket中的链表长度达到该值后,会转换为红黑树。 121 | func NewConcurrentRawHashMap(mm xmm.XMemory, cap uintptr, fact float64, treeSize uint64) (*ConcurrentRawHashMap, error) { 122 | if cap < 1 { 123 | return nil, errors.New("cap < 1") 124 | } 125 | var alignCap uintptr 126 | for i := 1; i < 64 && cap > alignCap; i++ { 127 | alignCap = 1 << uint(i) 128 | } 129 | cap = alignCap 130 | bucketsPtr, err := mm.AllocSlice(uintPtrSize, cap, cap) 131 | if err != nil { 132 | return nil, err 133 | } 134 | buckets := (*[]uintptr)(bucketsPtr) 135 | return &ConcurrentRawHashMap{buckets: buckets, initCap: uint64(cap), threshold: uint64(float64(cap) * fact), 136 | mm: mm, treeSize: treeSize}, nil 137 | } 138 | 139 | func (chm *ConcurrentRawHashMap) getBucket(h uint64, tab *[]uintptr) *Bucket { 140 | mask := uint64(cap(*tab) - 1) 141 | idx := h & mask 142 | _, _, bucket := chm.tabAt(tab, idx) 143 | if bucket != nil && bucket.forwarding && chm.transferIndex >= 0 { 144 | return chm.getBucket(h, bucket.newBuckets) 145 | } 146 | return bucket 147 | } 148 | 149 | // Get Fetch key from hashmap 150 | func (chm *ConcurrentRawHashMap) Get(key []byte) (val []byte, keyExists bool, err error) { 151 | h := BKDRHashWithSpread(key) 152 | bucket := chm.getBucket(h, chm.buckets) 153 | if bucket == nil { 154 | return nil, KeyNotExists, NotFound 155 | } 156 | if bucket.isTree { 157 | exist, value := bucket.Tree.Get(key) 158 | if !exist { 159 | return nil, KeyNotExists, NotFound 160 | } 161 | return value, KeyExists, nil 162 | } 163 | keySize := len(key) 164 | for cNode := bucket.Head; cNode != nil; cNode = cNode.Next { 165 | if keySize == len(cNode.Key) && bytes.Compare(key, cNode.Key) == 0 { 166 | return cNode.Value, KeyExists, nil 167 | } 168 | } 169 | return nil, KeyNotExists, NotFound 170 | } 171 | func (chm *ConcurrentRawHashMap) initForwardingEntries(newBuckets *[]uintptr, index uint64) (*ForwardingBucket, error) { 172 | entriesPtr, err := chm.mm.Alloc(_ForwardingBucketSize) 173 | if err != nil { 174 | return nil, err 175 | } 176 | entries := (*ForwardingBucket)(entriesPtr) 177 | chm.assignmentForwardingEntries(newBuckets, entries, index) 178 | return entries, nil 179 | } 180 | 181 | func (chm *ConcurrentRawHashMap) assignmentForwardingEntries(newBuckets *[]uintptr, entries *ForwardingBucket, index uint64) { 182 | entries.newBuckets = newBuckets 183 | entries.index = index 184 | entries.forwarding = true 185 | } 186 | 187 | func (chm *ConcurrentRawHashMap) initEntries(entry *entry.NodeEntry, idx uint64) (*Bucket, error) { 188 | ptr, err := chm.mm.Alloc(_BucketSize) 189 | if err != nil { 190 | return nil, err 191 | } 192 | entries := (*Bucket)(ptr) 193 | chm.assignmentEntries(entries, entry, idx) 194 | return entries, nil 195 | } 196 | 197 | func (chm *ConcurrentRawHashMap) assignmentEntries(entries *Bucket, entry *entry.NodeEntry, index uint64) { 198 | entries.index = index 199 | entries.Head = entry 200 | } 201 | 202 | func (chm *ConcurrentRawHashMap) getStride(length uint64) (stride uint64) { 203 | cpuNum := uint64(runtime.NumCPU()) 204 | if cpuNum > 1 { 205 | stride = (length >> 3) / cpuNum 206 | } else { 207 | stride = length 208 | } 209 | if stride < MinTransferStride { 210 | stride = MinTransferStride 211 | } 212 | return stride 213 | } 214 | 215 | func (chm *ConcurrentRawHashMap) resizeStamp(length uint64) (stride int64) { 216 | return -10000 - int64(length) 217 | } 218 | 219 | func (chm *ConcurrentRawHashMap) helpTransform(entry *entry.NodeEntry, bucket *Bucket, tab *[]uintptr) (currentBucket *Bucket, init bool, currentTab *[]uintptr, err error) { 220 | if bucket != nil && bucket.forwarding { 221 | if err := chm.reHashSize(tab); err != nil { 222 | return nil, init, nil, err 223 | } 224 | tabPtr := bucket.newBuckets 225 | bucket, swapped, err := chm.getAndInitBucket(entry, tabPtr) 226 | if err != nil { 227 | return nil, init, nil, err 228 | } 229 | if swapped { 230 | return nil, true, nil, err 231 | } 232 | return bucket, init, tabPtr, nil 233 | } 234 | return bucket, init, tab, nil 235 | } 236 | 237 | func (chm *ConcurrentRawHashMap) indexAndInitBucket(entry *entry.NodeEntry) (entries *Bucket, init bool, tabPtr *[]uintptr, err error) { 238 | tabPtr = chm.buckets 239 | bucket, swapped, err := chm.getAndInitBucket(entry, tabPtr) 240 | if err != nil { 241 | return nil, init, nil, err 242 | } 243 | if swapped { 244 | return bucket, true, tabPtr, nil 245 | } 246 | if bucket != nil && bucket.forwarding && chm.transferIndex >= 0 { 247 | bucket, swapped, tabPtr, err = chm.helpTransform(entry, bucket, tabPtr) 248 | if err != nil { 249 | return nil, init, nil, err 250 | } 251 | if swapped { 252 | return bucket, true, tabPtr, nil 253 | } 254 | } 255 | return bucket, init, tabPtr, nil 256 | } 257 | 258 | func (chm *ConcurrentRawHashMap) index(h uint64, length int) uint64 { 259 | idx := h & uint64(length-1) 260 | return idx 261 | } 262 | 263 | func (chm *ConcurrentRawHashMap) tabAt(buckets *[]uintptr, idx uint64) (*uintptr, uintptr, *Bucket) { 264 | addr := &((*buckets)[idx]) 265 | ptr := atomic.LoadUintptr(addr) 266 | if ptr == 0 { 267 | return addr, ptr, nil 268 | } 269 | bucket := (*Bucket)(unsafe.Pointer(ptr)) 270 | return addr, ptr, bucket 271 | } 272 | 273 | // cas 设置bucket 274 | func (chm *ConcurrentRawHashMap) getAndInitBucket(entry *entry.NodeEntry, tabPtr *[]uintptr) (bucket *Bucket, swapped bool, err error) { 275 | h := entry.Hash 276 | idx := chm.index(h, cap(*tabPtr)) 277 | // retry := 10改小后,出现该问题。 278 | addr, _, bucket := chm.tabAt(tabPtr, idx) 279 | if bucket != nil { 280 | return bucket, false, nil 281 | } 282 | entity, err := chm.initEntries(entry, idx) 283 | if err != nil { 284 | return nil, false, err 285 | } 286 | ptr := uintptr(unsafe.Pointer(entity)) 287 | atomic.CompareAndSwapUintptr(addr, 0, ptr) 288 | bucket = (*Bucket)(unsafe.Pointer(atomic.LoadUintptr(addr))) 289 | return bucket, swapped, nil 290 | } 291 | 292 | func (chm *ConcurrentRawHashMap) increaseSize() (newSize uint64) { 293 | retry := 30 294 | var swapped bool 295 | for !swapped && retry > 0 { 296 | retry-- 297 | oldVal := atomic.LoadUint64(&chm.size) 298 | newVal := oldVal + 1 299 | swapped = atomic.CompareAndSwapUint64(&chm.size, oldVal, newVal) 300 | if swapped { 301 | return newVal 302 | } 303 | } 304 | chm.lock.Lock() 305 | defer chm.lock.Unlock() 306 | size := chm.size + 1 307 | chm.size = size 308 | return size 309 | } 310 | 311 | func (chm *ConcurrentRawHashMap) createEntry(key []byte, val []byte, hash uint64) (*entry.NodeEntry, error) { 312 | entryPtr, err := chm.mm.Alloc(_NodeEntrySize) 313 | if err != nil { 314 | return nil, err 315 | } 316 | node := (*entry.NodeEntry)(entryPtr) 317 | keyPtr, valPtr, err := chm.mm.Copy2(key, val) 318 | if err != nil { 319 | return nil, err 320 | } 321 | chm.entryAssignment(keyPtr, valPtr, hash, node) 322 | return node, nil 323 | } 324 | 325 | func (chm *ConcurrentRawHashMap) entryAssignment(keyPtr []byte, valPtr []byte, hash uint64, entry *entry.NodeEntry) { 326 | entry.Key = keyPtr 327 | entry.Value = valPtr 328 | entry.Hash = hash 329 | } 330 | 331 | func (chm *ConcurrentRawHashMap) entryAssignmentCpy(keyPtr []byte, valPtr []byte, hash uint64, nodePtr unsafe.Pointer) error { 332 | source := entry.NodeEntry{Value: keyPtr, Key: valPtr, Hash: hash} 333 | offset := 40 // unsafe.Offsetof(source.Next) //40 334 | srcData := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&source)), Len: offset, Cap: offset})) 335 | dstData := (*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(nodePtr), Len: offset, Cap: offset})) 336 | if offset != copy(*dstData, *srcData) { 337 | return errors.New("incorrect copy length") // 拷贝长度不正确 338 | } 339 | return nil 340 | } 341 | 342 | func (chm *ConcurrentRawHashMap) putVal(key []byte, val []byte, h uint64) (*[]uintptr, error) { 343 | node, err := chm.createEntry(key, val, h) 344 | if err != nil { 345 | return nil, err 346 | } 347 | loop := true 348 | var tabPtr *[]uintptr 349 | for loop { 350 | bucket, init, newTabPtr, err := chm.indexAndInitBucket(node) 351 | if err != nil { 352 | return nil, err 353 | } 354 | tabPtr = newTabPtr 355 | if init { 356 | break 357 | } 358 | if loop, err = chm.PutBucketValue(bucket, node, tabPtr); err != nil { 359 | return nil, err 360 | } 361 | } 362 | return tabPtr, nil 363 | } 364 | 365 | // Put 将键值对添加到map中 366 | func (chm *ConcurrentRawHashMap) Put(key []byte, val []byte) error { 367 | h := BKDRHashWithSpread(key) 368 | tabPtr, err := chm.putVal(key, val, h) 369 | if err != nil { 370 | return err 371 | } 372 | size := chm.increaseSize() 373 | threshold := atomic.LoadUint64(&chm.threshold) 374 | if size >= threshold && size < maximumCapacity { 375 | return chm.reHashSize(tabPtr) 376 | } 377 | return nil 378 | } 379 | 380 | // Del 删除键值对 381 | func (chm *ConcurrentRawHashMap) Del(key []byte) error { 382 | h := BKDRHashWithSpread(key) 383 | return chm.delVal(key, h) 384 | } 385 | 386 | func (chm *ConcurrentRawHashMap) delVal(key []byte, h uint64) error { 387 | loop := true 388 | var tabPtr *[]uintptr 389 | tabPtr = chm.buckets 390 | for loop { 391 | idx := chm.index(h, cap(*tabPtr)) 392 | _, _, bucket := chm.tabAt(tabPtr, idx) 393 | if bucket == nil { 394 | return nil 395 | } 396 | if bucket != nil && bucket.forwarding { 397 | if err := chm.reHashSize(tabPtr); err != nil { 398 | return err 399 | } 400 | tabPtr = bucket.newBuckets 401 | idx = chm.index(h, cap(*tabPtr)) 402 | _, _, bucket = chm.tabAt(tabPtr, idx) 403 | continue 404 | } 405 | var removeNode *entry.NodeEntry 406 | func() { 407 | // 删除bucket中的数目 408 | bucket.rwLock.Lock() 409 | defer bucket.rwLock.Unlock() 410 | _, _, newBucket := chm.tabAt(tabPtr, chm.index(h, cap(*tabPtr))) 411 | if newBucket != bucket || (bucket != nil && bucket.forwarding) { 412 | return 413 | } 414 | if bucket.isTree { 415 | if node := bucket.Tree.Delete(key); node != nil { 416 | removeNode = node 417 | } 418 | } else { 419 | keySize := len(key) 420 | var pre *entry.NodeEntry 421 | for cNode := bucket.Head; cNode != nil; cNode = cNode.Next { 422 | if keySize == len(cNode.Key) && bytes.Compare(key, cNode.Key) == 0 { 423 | removeNode = cNode 424 | if pre == nil { 425 | bucket.Head = cNode.Next 426 | } else { 427 | pre.Next = cNode.Next 428 | } 429 | break 430 | } 431 | pre = cNode 432 | } 433 | } 434 | loop = false 435 | return 436 | }() 437 | if err := chm.freeEntry(removeNode); err != nil { 438 | return err 439 | } 440 | } 441 | return nil 442 | } 443 | 444 | // freeEntry todo 异步free 445 | func (chm *ConcurrentRawHashMap) freeEntry(removeNode *entry.NodeEntry) error { 446 | if removeNode == nil { 447 | return nil 448 | } 449 | if err := chm.mm.Free(uintptr(unsafe.Pointer(removeNode))); err != nil { 450 | return err 451 | } 452 | keySlice, valSlice := (*reflect.SliceHeader)(unsafe.Pointer(&removeNode.Key)), (*reflect.SliceHeader)(unsafe.Pointer(&removeNode.Value)) 453 | if err := chm.mm.Free(keySlice.Data); err != nil { 454 | return err 455 | } 456 | if err := chm.mm.Free(valSlice.Data); err != nil { 457 | return err 458 | } 459 | return nil 460 | } 461 | 462 | func (chm *ConcurrentRawHashMap) growTree(bucket *Bucket) error { 463 | if bucket.isTree || bucket.size < chm.treeSize { 464 | return nil 465 | } 466 | treePtr, err := chm.mm.Alloc(_TreeSize) 467 | if err != nil { 468 | return err 469 | } 470 | bucket.Tree = (*entry.Tree)(treePtr) 471 | bucket.Tree.SetComparator(entry.BytesAscSort) 472 | for node := bucket.Head; node != nil; { 473 | next := node.Next 474 | if err := bucket.Tree.Put(node); err != nil { 475 | return err 476 | } 477 | node = next 478 | } 479 | bucket.Head = nil 480 | bucket.isTree = true 481 | return nil 482 | } 483 | 484 | // PutBucketValue table的可见性问题,并发问题。 485 | func (chm *ConcurrentRawHashMap) PutBucketValue(bucket *Bucket, node *entry.NodeEntry, tab *[]uintptr) (loop bool, err error) { 486 | bucket.rwLock.Lock() 487 | defer bucket.rwLock.Unlock() 488 | idx := chm.index(node.Hash, cap(*tab)) 489 | _, _, newBucket := chm.tabAt(tab, idx) 490 | if newBucket != bucket || newBucket.forwarding { 491 | return true, nil 492 | } 493 | bucket = newBucket 494 | // 树化 495 | if err := chm.growTree(bucket); err != nil { 496 | return false, err 497 | } 498 | if bucket.isTree { 499 | if err := bucket.Tree.Put(node); err != nil { 500 | return false, err 501 | } 502 | return false, nil 503 | } 504 | key, val := node.Key, node.Value 505 | var last *entry.NodeEntry 506 | for node := bucket.Head; node != nil; node = node.Next { 507 | if len(key) == len(node.Key) && bytes.Compare(node.Key, key) == 0 { 508 | node.Value = val 509 | return false, nil 510 | } 511 | if node.Next == nil { 512 | last = node 513 | } 514 | } 515 | bucket.size += 1 516 | // 加入 517 | if last == nil { 518 | bucket.Head = node 519 | return false, nil 520 | } 521 | last.Next = node 522 | return false, nil 523 | } 524 | 525 | func (chm *ConcurrentRawHashMap) expandCap(tab *[]uintptr) (s *Snapshot, need bool) { 526 | old, size, transferIndex, threshold := uint64(cap(*chm.buckets)), atomic.LoadUint64(&chm.size), 527 | atomic.LoadUint64(&chm.transferIndex), atomic.LoadUint64(&chm.threshold) 528 | if size < threshold || transferIndex >= old { 529 | return nil, false 530 | } 531 | nextBuckets, buckets, sizeCtl := chm.nextBuckets, chm.buckets, atomic.LoadInt64(&chm.sizeCtl) 532 | // 正在扩容 533 | if sizeCtl < 0 && nextBuckets != nil && cap(*nextBuckets) == int(old)*2 { 534 | if sizeCtl >= 0 { 535 | return nil, false 536 | } 537 | // 当前扩容状态正确,开始扩容 538 | if unsafe.Pointer(tab) == unsafe.Pointer(buckets) && nextBuckets != nil { 539 | if atomic.CompareAndSwapInt64(&chm.sizeCtl, sizeCtl, sizeCtl+1) { 540 | return &Snapshot{buckets: buckets, sizeCtl: sizeCtl, nextBuckets: nextBuckets}, true 541 | } 542 | } 543 | return nil, false 544 | } 545 | // 未开始扩容的判断 546 | if sizeCtl >= 0 && (old<<1) > uint64(sizeCtl) && unsafe.Pointer(tab) == unsafe.Pointer(buckets) { 547 | newSizeCtl := chm.resizeStamp(old) + 2 548 | swapped := atomic.CompareAndSwapInt64(&chm.sizeCtl, sizeCtl, newSizeCtl) 549 | // 开始扩容 550 | if swapped { 551 | newCap := old << 1 552 | bucketsPtr, err := chm.mm.AllocSlice(uintPtrSize, uintptr(newCap), uintptr(newCap)) 553 | if err != nil { 554 | log.Printf("ConcurrentRawHashMap chm.mm.Alloc newCap:%d err:%s \n", newCap, err) 555 | return nil, false 556 | } 557 | nextBuckets = (*[]uintptr)(bucketsPtr) 558 | chm.nextBuckets = nextBuckets 559 | chm.transferIndex = 0 560 | return &Snapshot{buckets: buckets, sizeCtl: sizeCtl, nextBuckets: nextBuckets}, true 561 | } 562 | return nil, false 563 | } 564 | return nil, false 565 | } 566 | 567 | func (chm *ConcurrentRawHashMap) reHashSize(tab *[]uintptr) error { 568 | // cas锁 569 | snapshot, need := chm.expandCap(tab) 570 | if !need { 571 | return nil 572 | } 573 | currentCap := uint64(cap(*snapshot.buckets)) 574 | buckets := snapshot.nextBuckets 575 | // 取当前内容oldBuckets,在oldBuckets中利用bucket的lock来避免同时操作。 576 | err := chm.reSizeBuckets(snapshot) 577 | if err != nil { 578 | return err 579 | } 580 | for { 581 | sizeCtl := atomic.LoadInt64(&chm.sizeCtl) 582 | if sizeCtl >= 0 { 583 | return nil 584 | } 585 | if atomic.CompareAndSwapInt64(&chm.sizeCtl, sizeCtl, sizeCtl-1) { 586 | if sizeCtl != chm.resizeStamp(currentCap)+2 { 587 | return nil 588 | } 589 | break 590 | } 591 | } 592 | return func() error { 593 | oldBuckets, tabPtr := chm.buckets, unsafe.Pointer(&chm.buckets) 594 | bucketsAddr := (*unsafe.Pointer)(tabPtr) 595 | old := (*reflect.SliceHeader)(atomic.LoadPointer(bucketsAddr)) 596 | if old.Cap != int(currentCap) { 597 | return nil 598 | } 599 | if atomic.CompareAndSwapPointer(bucketsAddr, unsafe.Pointer(old), unsafe.Pointer(buckets)) { 600 | // 更换内容赋值 601 | chm.nextBuckets = nil 602 | newCap := currentCap << 1 603 | atomic.StoreInt64(&chm.sizeCtl, int64(newCap)) 604 | chm.threshold = chm.threshold << 1 605 | chm.reSizeGen += 1 606 | if err := chm.freeBuckets(oldBuckets); err != nil { 607 | log.Printf("freeBuckets err:%s\n", err) 608 | } 609 | return err 610 | } 611 | return nil 612 | }() 613 | } 614 | 615 | /* 616 | todo 异步free 清除内存 617 | buckets清除旧的(resize结束,并无get读引用【引入重试来解决该问题】) 618 | 3、Bucket清除 619 | 4、元素清除*/ 620 | func (chm *ConcurrentRawHashMap) freeBuckets(buckets *[]uintptr) error { 621 | for _, ptr := range *buckets { 622 | if ptr < 1 { 623 | continue 624 | } 625 | if err := chm.mm.Free(ptr); err != nil { 626 | log.Printf("freeBuckets Free element(%d) err:%s\n", ptr, err) 627 | } 628 | } 629 | if err := chm.mm.Free(uintptr(unsafe.Pointer(buckets))); err != nil { 630 | return err 631 | } 632 | return nil 633 | } 634 | 635 | func (chm *ConcurrentRawHashMap) increaseTransferIndex(cap uint64, stride uint64) (offset uint64, over bool) { 636 | for { 637 | transferIndex := atomic.LoadUint64(&chm.transferIndex) 638 | if transferIndex >= cap { 639 | return 0, true 640 | } 641 | swapped := atomic.CompareAndSwapUint64(&chm.transferIndex, transferIndex, transferIndex+stride) 642 | if swapped { 643 | return transferIndex, false 644 | } 645 | } 646 | } 647 | 648 | func (chm *ConcurrentRawHashMap) reSizeBuckets(s *Snapshot) error { 649 | newBuckets, oldBuckets, currentCap := s.nextBuckets, s.buckets, uint64(cap(*s.buckets)) 650 | for { 651 | stride := chm.getStride(currentCap) 652 | offset, over := chm.increaseTransferIndex(currentCap, stride) 653 | if over { 654 | return nil 655 | } 656 | maxIndex := offset + stride 657 | if maxIndex > currentCap { 658 | maxIndex = currentCap 659 | } 660 | for index := offset; index < maxIndex; index++ { 661 | var err error 662 | loop := true 663 | for loop { 664 | addr := &((*oldBuckets)[index]) 665 | entries := (*Bucket)(unsafe.Pointer(atomic.LoadUintptr(addr))) 666 | if entries == nil { 667 | forwardingEntries, err := chm.initForwardingEntries(newBuckets, index) 668 | if err != nil { 669 | log.Printf("ERROR reSizeBucket initForwardingEntries err:%s\n", err) 670 | return err 671 | } 672 | ptr := uintptr(unsafe.Pointer(forwardingEntries)) 673 | if atomic.CompareAndSwapUintptr(addr, 0, ptr) { 674 | break 675 | } else { 676 | entries = (*Bucket)(unsafe.Pointer(atomic.LoadUintptr(addr))) 677 | } 678 | } 679 | if loop, err = chm.reSizeBucket(entries, s); err != nil { 680 | log.Printf("ERROR reSizeBucket oldEntries err:%s\n", err) 681 | } 682 | } 683 | } 684 | } 685 | } 686 | 687 | // Chain .链接 688 | type Chain struct { 689 | Head *entry.NodeEntry 690 | Tail *entry.NodeEntry 691 | } 692 | 693 | // Add 添加节点 694 | func (c *Chain) Add(node *entry.NodeEntry) { 695 | if c.Head == nil { 696 | c.Head = node 697 | } 698 | if c.Tail != nil { 699 | c.Tail.Next = node 700 | } 701 | c.Tail = node 702 | } 703 | 704 | // GetHead 获取head节点 705 | func (c *Chain) GetHead() *entry.NodeEntry { 706 | if c.Tail != nil { 707 | c.Tail.Next = nil 708 | } 709 | return c.Head 710 | } 711 | 712 | func (chm *ConcurrentRawHashMap) reSizeBucket(entries *Bucket, s *Snapshot) (loop bool, err error) { 713 | entries.rwLock.Lock() 714 | defer entries.rwLock.Unlock() 715 | oldBuckets, newBuckets := s.buckets, s.nextBuckets 716 | currentCap := uint64(cap(*s.buckets)) 717 | tabPtr, nextTabPtr := unsafe.Pointer(s.buckets), unsafe.Pointer(s.nextBuckets) 718 | _, _, currentEntries := chm.tabAt(oldBuckets, entries.index) 719 | // 判断newBuckets为chm.newBuckets,判断newBuckets为chm.newBuckets 720 | if entries.forwarding || tabPtr != unsafe.Pointer(chm.buckets) || 721 | nextTabPtr != unsafe.Pointer(chm.nextBuckets) || entries != currentEntries { 722 | return false, nil 723 | } 724 | if entries != nil && ((!entries.isTree && entries.Head == nil) || (entries.isTree && entries.Tree == nil)) { 725 | entries.newBuckets = newBuckets 726 | entries.forwarding = true 727 | return false, nil 728 | } 729 | mask := (currentCap << 1) - 1 730 | index := entries.index 731 | var oldIndex, newIndex = index, currentCap + index 732 | oldChains, newChains, err := chm.spliceEntry2(entries, mask) 733 | if err != nil { 734 | return true, err 735 | } 736 | oldBucket, newBucket, err := chm.spliceBucket2(oldChains, newChains) 737 | if err != nil { 738 | return false, err 739 | } 740 | if oldBucket != nil { 741 | oldBucket.index = oldIndex 742 | (*newBuckets)[oldIndex] = uintptr(unsafe.Pointer(oldBucket)) 743 | } 744 | if newBucket != nil { 745 | newBucket.index = newIndex 746 | (*newBuckets)[newIndex] = uintptr(unsafe.Pointer(newBucket)) 747 | } 748 | entries.newBuckets = newBuckets 749 | entries.forwarding = true 750 | entries.Head = nil 751 | return false, nil 752 | } 753 | 754 | // TreeSplice . 755 | func (chm *ConcurrentRawHashMap) TreeSplice(node *entry.NodeEntry, index uint64, mask uint64, oldChains, newChains *Chain) { 756 | if node == nil { 757 | return 758 | } 759 | if node.Hash&mask == index { 760 | oldChains.Add(node) 761 | } else { 762 | newChains.Add(node) 763 | } 764 | chm.TreeSplice(node.Left(), index, mask, oldChains, newChains) 765 | chm.TreeSplice(node.Right(), index, mask, oldChains, newChains) 766 | } 767 | 768 | // spliceEntry2 将Entry拆分为两个列表 769 | func (chm *ConcurrentRawHashMap) spliceEntry2(entries *Bucket, mask uint64) (old *entry.NodeEntry, new *entry.NodeEntry, err error) { 770 | var oldHead, oldTail, newHead, newTail *entry.NodeEntry 771 | index := entries.index 772 | if entries.isTree { 773 | var oldChains, newChains Chain 774 | chm.TreeSplice(entries.Tree.GetRoot(), index, mask, &oldChains, &newChains) 775 | oldHead, newHead = oldChains.GetHead(), newChains.GetHead() 776 | // 删除现在的tree内容 777 | if err := chm.mm.Free(uintptr(unsafe.Pointer(entries.Tree))); err != nil { 778 | return nil, nil, err 779 | } 780 | } else { 781 | for nodeEntry := entries.Head; nodeEntry != nil; nodeEntry = nodeEntry.Next { 782 | idx := nodeEntry.Hash & mask 783 | if idx == index { 784 | if oldHead == nil { 785 | oldHead = nodeEntry 786 | } else if oldTail != nil { 787 | oldTail.Next = nodeEntry 788 | } 789 | oldTail = nodeEntry 790 | } else { 791 | if newHead == nil { 792 | newHead = nodeEntry 793 | } else if newTail != nil { 794 | newTail.Next = nodeEntry 795 | } 796 | newTail = nodeEntry 797 | } 798 | } 799 | } 800 | if newTail != nil { 801 | newTail.Next = nil 802 | } 803 | if oldTail != nil { 804 | oldTail.Next = nil 805 | } 806 | return oldHead, newHead, nil 807 | } 808 | 809 | // 性能更好 810 | func (chm *ConcurrentRawHashMap) spliceBucket2(old *entry.NodeEntry, new *entry.NodeEntry) (oldBucket *Bucket, newBucket *Bucket, err error) { 811 | if old != nil { 812 | head, index := old, uint64(0) 813 | item, err := chm.initEntries(head, index) 814 | if err != nil { 815 | return nil, nil, err 816 | } 817 | oldBucket = item 818 | } 819 | 820 | if new != nil { 821 | head, index := new, uint64(0) 822 | item, err := chm.initEntries(head, index) 823 | if err != nil { 824 | return nil, nil, err 825 | } 826 | newBucket = item 827 | } 828 | return 829 | } 830 | 831 | func (chm *ConcurrentRawHashMap) indexBucket(idx int, tab *[]uintptr) (bucket *Bucket, outIndex bool) { 832 | if idx >= cap(*tab) { 833 | return nil, false 834 | } 835 | _, _, bucket = chm.tabAt(tab, uint64(idx)) 836 | if bucket != nil && bucket.forwarding && chm.transferIndex >= 0 { 837 | return chm.indexBucket(idx, bucket.newBuckets) 838 | } 839 | return bucket, true 840 | } 841 | 842 | func (chm *ConcurrentRawHashMap) ForEach(fun func(key, val []byte) error) error { 843 | bucket, exist := chm.indexBucket(0, chm.buckets) 844 | for i := 1; exist; i++ { 845 | if bucket != nil { 846 | if tree := bucket.Tree; bucket.isTree && tree != nil { 847 | if err := chm.TreeForEach(fun, tree.GetRoot()); err != nil { 848 | return err 849 | } 850 | } else { 851 | for cNode := bucket.Head; cNode != nil; cNode = cNode.Next { 852 | if err := fun(cNode.Key, cNode.Value); err != nil { 853 | return err 854 | } 855 | } 856 | } 857 | } 858 | bucket, exist = chm.indexBucket(i, chm.buckets) 859 | } 860 | return nil 861 | } 862 | 863 | func (chm *ConcurrentRawHashMap) TreeForEach(fun func(key, val []byte) error, node *entry.NodeEntry) error { 864 | if node == nil { 865 | return nil 866 | } 867 | if err := fun(node.Key, node.Value); err != nil { 868 | return err 869 | } 870 | chm.TreeForEach(fun, node.Left()) 871 | chm.TreeForEach(fun, node.Right()) 872 | return nil 873 | } 874 | 875 | // BKDRHashWithSpread . 31 131 1313 13131 131313 etc.. 876 | func BKDRHashWithSpread(str []byte) uint64 { 877 | seed := uint64(131) 878 | hash := uint64(0) 879 | for i := 0; i < len(str); i++ { 880 | hash = (hash * seed) + uint64(str[i]) 881 | } 882 | return hash ^ (hash>>16)&0x7FFFFFFF 883 | } 884 | --------------------------------------------------------------------------------