From: qydysky Date: Wed, 17 Feb 2021 16:24:18 +0000 (+0800) Subject: syncMap X-Git-Tag: v0.4.1 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=20cc67627872e109b2ed965a3dad6dae7d688503;p=part%2F.git syncMap --- diff --git a/map/Map.go b/map/Map.go new file mode 100644 index 0000000..16d9c81 --- /dev/null +++ b/map/Map.go @@ -0,0 +1,132 @@ +package part + +import ( + "unsafe" + "runtime" + "sync/atomic" +) + +type Map struct { + lock pLock //sync + num int + m map[interface{}]*ptr + readOnly atomic.Value //sync +} + +func (t *Map) Store(k,v interface{}) { + + m,_ := t.readOnly.Load().(map[interface{}]*ptr) + if p,ok := m[k];ok{ + p.tryStore(&v) + return + } + + t.lock.Lock() + + m,_ = t.readOnly.Load().(map[interface{}]*ptr) + if p,ok := m[k];ok{ + p.tryStore(&v) + } else if p,ok := t.m[k];ok{ + p.tryStore(&v) + } else { + if t.m == nil {t.mapFrom(&m)} + t.m[k] = &ptr{p:unsafe.Pointer(&v)} + t.num += 1 + } + + t.lock.Unlock() +} + +func (t *Map) Load(k interface{}) (v interface{}) { + + m,_ := t.readOnly.Load().(map[interface{}]*ptr) + p,ok := m[k] + if !ok && t.num != 0{ + t.lock.Lock() + + m,_ = t.readOnly.Load().(map[interface{}]*ptr) + p,ok = m[k] + if !ok && t.num != 0{ + if t.m == nil {t.mapFrom(&m)} + p,ok = t.m[k] + if ok && t.num > 1e3{ + t.readOnly.Store(t.m) + t.m = nil + t.num = 0 + } + } + + t.lock.Unlock() + } + + if !ok{ + return nil + } + + return p.tryLoad() +} + +func (t *Map) Delete(k interface{}) { + m,_ := t.readOnly.Load().(map[interface{}]*ptr) + + if p,ok := m[k];ok && p != nil{ + delete(m, k) + return + } + + t.lock.Lock() + + delete(t.m, k) + + t.lock.Unlock() +} + +func (t *Map) Len() int { + m,_ := t.readOnly.Load().(map[interface{}]*ptr) + return len(m) + t.num +} + +func (t *Map) mapFrom(from *map[interface{}]*ptr) { + if t.m == nil {t.m = make(map[interface{}]*ptr)} + for k,v := range *from{ + if v == nil {continue} + t.m[k] = v + } +} + +type ptr struct { + p unsafe.Pointer +} + +func (t *ptr) tryStore(v *interface{}) { + t.p = unsafe.Pointer(v) + // atomic.StorePointer(&t.p, unsafe.Pointer(v)) +} + +func (t *ptr) tryLoad() (interface{}) { + // p := atomic.LoadPointer(&t.p) + if t.p == nil{ + return nil + } + return *(*interface{})(t.p) +} + +type pLock struct{ + i unsafe.Pointer + busy unsafe.Pointer +} + +func (l *pLock) Lock() { + if l.busy == nil{l.busy = unsafe.Pointer(&struct{}{})} + for !atomic.CompareAndSwapPointer(&l.i, nil, l.busy) { + runtime.Gosched() + } +} + +func (l *pLock) Locking() (bool) { + return atomic.LoadPointer(&l.i) != nil +} + +func (l *pLock) Unlock() { + atomic.CompareAndSwapPointer(&l.i, l.busy, nil) +} diff --git a/map/Map_test.go b/map/Map_test.go new file mode 100644 index 0000000..d5aa198 --- /dev/null +++ b/map/Map_test.go @@ -0,0 +1,218 @@ +package part + +import ( + "sync" + "testing" +) + +func Test_customMap(t *testing.T) { + var c Map + //set + c.Store(0, 3) + if c.Load(0) != 3{t.Error(`1`)} + //change + c.Store(0, 1) + if c.Load(0) != 1{t.Error(`2`)} + //del + c.Store(0, nil) + if c.Load(0) != nil{t.Error(`3`)} +} + +func Benchmark_customMap_Set(b *testing.B) { + var c Map + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, nil) + w.Done() + }(i) + } + w.Wait() +} + +func Benchmark_customMap_Del(b *testing.B) { + c := new(Map) + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, 0) + w.Done() + }(i) + } + w.Wait() + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Delete(index) + w.Done() + }(i) + } + w.Wait() +} + +func Benchmark_customMap_Get(b *testing.B) { + var c Map + var w = &sync.WaitGroup{} + var t = b.N + + w.Add(t) + b.ResetTimer() + for i := 0; i < t; i++ { + go func(index int) { + c.Store(index, index) + w.Done() + }(i) + } + w.Wait() + + b.ResetTimer() + + w.Add(t) + b.ResetTimer() + for i := 0; i < t; i++ { + go func(index int) { + if c.Load(index).(int) != index { + b.Error("q") + } + w.Done() + }(i) + } + w.Wait() +} + +func Benchmark_customMap_SetGet(b *testing.B) { + var c Map + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, index) + w.Done() + }(i) + } + w.Wait() + + b.Log(c.Len()) + + w.Add(b.N) + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, index+1) + w.Done() + }(i) + go func(index int) { + if t,ok := c.Load(index).(int);!ok || t != index && t != index+1{ + b.Error(`E`, index, t) + } + w.Done() + }(i) + } + w.Wait() +} + +func Benchmark_syncMap_Set(b *testing.B) { + c:=new(sync.Map) + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, nil) + w.Done() + }(i) + } + w.Wait() + } + + func Benchmark_syncMap_Del(b *testing.B) { + c := new(sync.Map) + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, 0) + w.Done() + }(i) + } + w.Wait() + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Delete(index) + w.Done() + }(i) + } + w.Wait() +} + +func Benchmark_syncMap_Get(b *testing.B) { + c := new(sync.Map) + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, 0) + w.Done() + }(i) + } + w.Wait() + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + if v,ok := c.Load(index);!ok || v.(int) != 0 {b.Error("q")} + w.Done() + }(i) + } + w.Wait() +} + +func Benchmark_syncMap_SetGet(b *testing.B) { + c := new(sync.Map) + var w = &sync.WaitGroup{} + + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, 0) + w.Done() + }(i) + } + w.Wait() + + w.Add(b.N) + w.Add(b.N) + b.ResetTimer() + for i := 0; i < b.N; i++ { + go func(index int) { + c.Store(index, 1) + w.Done() + }(i) + go func(index int) { + c.Load(index) + w.Done() + }(i) + } + w.Wait() +}