This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils
import (
"sync/atomic"
)
type CounterType interface {
int32 | int64 | uint32 | uint64
}
type Counter[T CounterType] struct {
values *Values[T]
}
func NewCounter[T CounterType]() *Counter[T] {
return &Counter[T]{
values: NewValues[T](func() T {
return 0
}),
}
}
func (this *Counter[T]) Add(delta T) {
this.values.DoUnsafe(func(value *T) {
switch x := any(value).(type) {
case *int32:
atomic.AddInt32(x, int32(delta))
case *int64:
atomic.AddInt64(x, int64(delta))
case *uint32:
atomic.AddUint32(x, uint32(delta))
case *uint64:
atomic.AddUint64(x, uint64(delta))
}
})
}
func (this *Counter[T]) Load() T {
var result T
this.values.DoUnsafeAll(func(value T) {
result += value
})
return result
}

View File

@@ -0,0 +1,56 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils_test
import (
syncutils "github.com/TeaOSLab/EdgeNode/internal/utils/sync"
"runtime"
"sync"
"sync/atomic"
"testing"
)
func TestNewCounter(t *testing.T) {
var counter = syncutils.NewCounter[uint64]()
counter.Add(1)
counter.Add(2)
var wg = sync.WaitGroup{}
wg.Add(1024)
for i := 0; i < 1024; i++ {
go func() {
defer wg.Done()
counter.Add(1)
}()
}
wg.Wait()
t.Log(counter.Load())
}
func BenchmarkCounter_Add(b *testing.B) {
runtime.GOMAXPROCS(128)
var counter = syncutils.NewCounter[int64]()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
counter.Add(1)
counter.Load()
}
})
}
func BenchmarkCounter_Add_Atomic(b *testing.B) {
runtime.GOMAXPROCS(128)
var value int64
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
atomic.AddInt64(&value, 1)
}
})
}

View File

@@ -0,0 +1,136 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils
import (
"runtime"
"sync"
)
type KType interface {
int | int16 | int32 | int64 | uint | uint16 | uint32 | uint64 | uintptr
}
type VType interface {
any
}
type IntMap[K KType, V VType] struct {
count int
m []map[K]V
lockers []*sync.RWMutex
}
func NewIntMap[K KType, V VType]() *IntMap[K, V] {
var count = runtime.NumCPU() * 8
if count <= 0 {
count = 32
}
var m = []map[K]V{}
var lockers = []*sync.RWMutex{}
for i := 0; i < count; i++ {
m = append(m, map[K]V{})
lockers = append(lockers, &sync.RWMutex{})
}
return &IntMap[K, V]{
count: count,
m: m,
lockers: lockers,
}
}
func (this *IntMap[K, V]) Put(k K, v V) {
var index = this.index(k)
this.lockers[index].Lock()
this.m[index][k] = v
this.lockers[index].Unlock()
}
func (this *IntMap[K, V]) PutCompact(k K, v V, compactFunc func(oldV V, newV V) V) {
var index = this.index(k)
this.lockers[index].Lock()
// 再次检查是否已经存在,如果已经存在则合并
oldV, ok := this.m[index][k]
if ok {
this.m[index][k] = compactFunc(oldV, v)
} else {
this.m[index][k] = v
}
this.lockers[index].Unlock()
}
func (this *IntMap[K, V]) Has(k K) bool {
var index = this.index(k)
this.lockers[index].RLock()
_, ok := this.m[index][k]
this.lockers[index].RUnlock()
return ok
}
func (this *IntMap[K, V]) Get(k K) (value V) {
var index = this.index(k)
this.lockers[index].RLock()
value = this.m[index][k]
this.lockers[index].RUnlock()
return
}
func (this *IntMap[K, V]) GetOk(k K) (value V, ok bool) {
var index = this.index(k)
this.lockers[index].RLock()
value, ok = this.m[index][k]
this.lockers[index].RUnlock()
return
}
func (this *IntMap[K, V]) Delete(k K) {
var index = this.index(k)
this.lockers[index].Lock()
delete(this.m[index], k)
this.lockers[index].Unlock()
}
func (this *IntMap[K, V]) DeleteUnsafe(k K) {
var index = this.index(k)
delete(this.m[index], k)
}
func (this *IntMap[K, V]) Len() int {
var l int
for i := 0; i < this.count; i++ {
this.lockers[i].RLock()
l += len(this.m[i])
this.lockers[i].RUnlock()
}
return l
}
func (this *IntMap[K, V]) ForEachRead(iterator func(k K, v V)) {
for i := 0; i < this.count; i++ {
this.lockers[i].RLock()
for k, v := range this.m[i] {
iterator(k, v)
}
this.lockers[i].RUnlock()
}
}
func (this *IntMap[K, V]) ForEachWrite(iterator func(k K, v V)) {
for i := 0; i < this.count; i++ {
this.lockers[i].Lock()
for k, v := range this.m[i] {
iterator(k, v)
}
this.lockers[i].Unlock()
}
}
func (this *IntMap[K, V]) index(k K) int {
var index = int(k % K(this.count))
if index < 0 {
index = -index
}
return index
}

View File

@@ -0,0 +1,125 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils_test
import (
"github.com/TeaOSLab/EdgeNode/internal/stats"
syncutils "github.com/TeaOSLab/EdgeNode/internal/utils/sync"
"github.com/iwind/TeaGo/assert"
"github.com/iwind/TeaGo/types"
"sync"
"testing"
)
func TestIntMap(t *testing.T) {
var a = assert.NewAssertion(t)
var m = syncutils.NewIntMap[int, string]()
m.Put(1, "1")
a.IsTrue(m.Has(1))
a.IsFalse(m.Has(2))
m.Put(-1, "-1")
t.Log(m.Get(-1))
t.Log(m.Len(), "values")
{
a.IsTrue(m.Has(-1))
m.Delete(-1)
a.IsFalse(m.Has(-1))
}
t.Log(m.Len(), "values")
}
func TestInt64Map(t *testing.T) {
var a = assert.NewAssertion(t)
var m = syncutils.NewIntMap[int64, string]()
m.Put(1, "1")
a.IsTrue(m.Has(1))
a.IsFalse(m.Has(2))
m.Put(-1, "-1")
t.Log(m.Get(-1))
t.Log(m.Len(), "values")
{
a.IsTrue(m.Has(-1))
m.Delete(-1)
a.IsFalse(m.Has(-1))
}
m.Put(1024000000, "large int")
t.Log(m.Get(1024000000))
t.Log(m.Len(), "values")
}
func TestIntMap_PutCompact(t *testing.T) {
var a = assert.NewAssertion(t)
var m = syncutils.NewIntMap[int, string]()
m.Put(1, "a")
m.Put(1, "b")
a.IsTrue(m.Get(1) == "b")
m.PutCompact(1, "c", func(oldV string, newV string) string {
return oldV + newV
})
a.IsTrue(m.Get(1) == "bc")
}
func TestIntMap_ForEachRead(t *testing.T) {
var m = syncutils.NewIntMap[int, string]()
for i := 0; i < 100; i++ {
m.Put(i, "v"+types.String(i))
}
t.Log(m.Len())
m.ForEachRead(func(k int, v string) {
t.Log(k, v)
})
}
func TestIntMap_ForEachWrite(t *testing.T) {
var m = syncutils.NewIntMap[int, string]()
for i := 0; i < 100; i++ {
m.Put(i, "v"+types.String(i))
}
t.Log(m.Len())
m.ForEachRead(func(k int, v string) {
t.Log(k, v)
m.DeleteUnsafe(k)
})
t.Log(m.Len(), "elements left")
}
func BenchmarkNewIntMap(b *testing.B) {
var m = syncutils.NewIntMap[int, *stats.BandwidthStat]()
b.RunParallel(func(pb *testing.PB) {
var i int
for pb.Next() {
i++
m.Put(i, &stats.BandwidthStat{ServerId: 100})
_ = m.Get(i + 100)
}
})
}
func BenchmarkNewIntMap2(b *testing.B) {
var m = map[int]*stats.BandwidthStat{}
var locker = sync.RWMutex{}
b.RunParallel(func(pb *testing.PB) {
var i int
for pb.Next() {
i++
locker.Lock()
m[i] = &stats.BandwidthStat{ServerId: 100}
locker.Unlock()
locker.RLock()
_ = m[i+100]
locker.RUnlock()
}
})
}

View File

@@ -0,0 +1,56 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils
import (
"sync"
)
type RWMutex struct {
lockers []*sync.RWMutex
countLockers int
}
func NewRWMutex(count int) *RWMutex {
if count <= 0 {
count = 1
}
var lockers = []*sync.RWMutex{}
for i := 0; i < count; i++ {
lockers = append(lockers, &sync.RWMutex{})
}
return &RWMutex{
lockers: lockers,
countLockers: len(lockers),
}
}
func (this *RWMutex) Lock(index int) {
this.lockers[index%this.countLockers].Lock()
}
func (this *RWMutex) Unlock(index int) {
this.lockers[index%this.countLockers].Unlock()
}
func (this *RWMutex) RLock(index int) {
this.lockers[index%this.countLockers].RLock()
}
func (this *RWMutex) RUnlock(index int) {
this.lockers[index%this.countLockers].RUnlock()
}
func (this *RWMutex) TryLock(index int) bool {
return this.lockers[index%this.countLockers].TryLock()
}
func (this *RWMutex) TryRLock(index int) bool {
return this.lockers[index%this.countLockers].TryRLock()
}
func (this *RWMutex) RWMutex(index int) *sync.RWMutex {
return this.lockers[index%this.countLockers]
}

View File

@@ -0,0 +1,47 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils_test
import (
syncutils "github.com/TeaOSLab/EdgeNode/internal/utils/sync"
"runtime"
"sync"
"testing"
"time"
)
func TestNewRWMutex(t *testing.T) {
var locker = syncutils.NewRWMutex(runtime.NumCPU())
locker.Lock(1)
t.Log(locker.TryLock(1))
locker.Unlock(1)
t.Log(locker.TryLock(1))
}
func BenchmarkRWMutex_Lock(b *testing.B) {
var locker = syncutils.NewRWMutex(runtime.NumCPU())
b.RunParallel(func(pb *testing.PB) {
var i = 0
for pb.Next() {
i++
locker.Lock(i)
time.Sleep(1 * time.Millisecond)
locker.Unlock(i)
}
})
}
func BenchmarkRWMutex_Lock2(b *testing.B) {
var locker = &sync.Mutex{}
b.RunParallel(func(pb *testing.PB) {
var i = 0
for pb.Next() {
i++
locker.Lock()
time.Sleep(1 * time.Millisecond)
locker.Unlock()
}
})
}

View File

@@ -0,0 +1,91 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils
import (
"github.com/TeaOSLab/EdgeNode/internal/utils/procs"
"runtime"
"sync"
)
type Values[T any] struct {
count int
values []T
muList []*sync.RWMutex
}
func NewValues[T any](newFn func() T) *Values[T] {
var countProcs = runtime.GOMAXPROCS(0)
if countProcs < 0 {
countProcs = 32
}
var values []T
for i := 0; i < countProcs; i++ {
values = append(values, newFn())
}
var muList []*sync.RWMutex
for i := 0; i < countProcs; i++ {
muList = append(muList, &sync.RWMutex{})
}
return &Values[T]{
count: countProcs,
values: values,
muList: muList,
}
}
func (this *Values[T]) Len() int {
return this.count
}
func (this *Values[T]) DoReadAll(f func(value T)) {
for i := 0; i < this.count; i++ {
var mu = this.muList[i]
var value = this.values[i]
mu.RLock()
f(value)
mu.RUnlock()
}
}
func (this *Values[T]) DoRead(f func(value T)) {
var procId = procs.Id()
var mu = this.muList[procId]
var value = this.values[procId]
mu.RLock()
f(value)
mu.RUnlock()
}
func (this *Values[T]) DoWriteAll(f func(value T)) {
for i := 0; i < this.count; i++ {
var mu = this.muList[i]
var value = this.values[i]
mu.Lock()
f(value)
mu.Unlock()
}
}
func (this *Values[T]) DoWrite(f func(value T)) {
var procId = procs.Id()
var mu = this.muList[procId]
var value = this.values[procId]
mu.Lock()
f(value)
mu.Unlock()
}
func (this *Values[T]) DoUnsafe(f func(value *T)) {
var procId = procs.Id()
f(&this.values[procId])
}
func (this *Values[T]) DoUnsafeAll(f func(value T)) {
for _, value := range this.values {
f(value)
}
}

View File

@@ -0,0 +1,58 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package syncutils_test
import (
syncutils "github.com/TeaOSLab/EdgeNode/internal/utils/sync"
"math/rand"
"runtime"
"testing"
)
func TestNewValues_Int(t *testing.T) {
var value = -1
var values = syncutils.NewValues[int](func() int {
value++
return value
})
t.Log(values.Len())
values.DoRead(func(value int) {
t.Log("value at 1:", value)
})
values.DoWrite(func(value int) {
t.Log("value at 2:", value)
})
}
func TestNewValues_Map(t *testing.T) {
var values = syncutils.NewValues[map[int]bool](func() map[int]bool {
return map[int]bool{}
})
t.Log(values.Len())
values.DoWrite(func(value map[int]bool) {
value[123] = true
value[456] = false
})
values.DoRead(func(value map[int]bool) {
t.Log("value at 0:", value)
})
}
func BenchmarkValues_Map(b *testing.B) {
runtime.GOMAXPROCS(32)
var values = syncutils.NewValues[map[int]bool](func() map[int]bool {
return map[int]bool{}
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
values.DoWrite(func(value map[int]bool) {
value[rand.Int()%10_000] = true
})
}
})
}