mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
perf(slice): make the IndexOf function thread-safe (#263)
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/random"
|
"github.com/duke-git/lancet/v2/random"
|
||||||
@@ -21,6 +22,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
memoryHashMap = make(map[string]map[any]int)
|
memoryHashMap = make(map[string]map[any]int)
|
||||||
memoryHashCounter = make(map[string]int)
|
memoryHashCounter = make(map[string]int)
|
||||||
|
muForMemoryHash sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// Contain check if the target value is in the slice or not.
|
// Contain check if the target value is in the slice or not.
|
||||||
@@ -1174,23 +1176,46 @@ func IndexOf[T comparable](arr []T, val T) int {
|
|||||||
limit := 10
|
limit := 10
|
||||||
// gets the hash value of the array as the key of the hash table.
|
// gets the hash value of the array as the key of the hash table.
|
||||||
key := fmt.Sprintf("%p", arr)
|
key := fmt.Sprintf("%p", arr)
|
||||||
|
|
||||||
|
muForMemoryHash.RLock()
|
||||||
// determines whether the hash table is empty. If so, the hash table is created.
|
// determines whether the hash table is empty. If so, the hash table is created.
|
||||||
if memoryHashMap[key] == nil {
|
if memoryHashMap[key] == nil {
|
||||||
memoryHashMap[key] = make(map[any]int)
|
|
||||||
// iterate through the array, adding the value and index of each element to the hash table.
|
muForMemoryHash.RUnlock()
|
||||||
for i := len(arr) - 1; i >= 0; i-- {
|
muForMemoryHash.Lock()
|
||||||
memoryHashMap[key][arr[i]] = i
|
|
||||||
|
if memoryHashMap[key] == nil {
|
||||||
|
memoryHashMap[key] = make(map[any]int)
|
||||||
|
// iterate through the array, adding the value and index of each element to the hash table.
|
||||||
|
for i := len(arr) - 1; i >= 0; i-- {
|
||||||
|
memoryHashMap[key][arr[i]] = i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
muForMemoryHash.Unlock()
|
||||||
|
} else {
|
||||||
|
muForMemoryHash.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
muForMemoryHash.Lock()
|
||||||
// update the hash table counter.
|
// update the hash table counter.
|
||||||
memoryHashCounter[key]++
|
memoryHashCounter[key]++
|
||||||
|
muForMemoryHash.Unlock()
|
||||||
|
|
||||||
// use the hash table to find the specified value. If found, the index is returned.
|
// use the hash table to find the specified value. If found, the index is returned.
|
||||||
if index, ok := memoryHashMap[key][val]; ok {
|
muForMemoryHash.RLock()
|
||||||
|
index, ok := memoryHashMap[key][val]
|
||||||
|
muForMemoryHash.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
muForMemoryHash.RLock()
|
||||||
// calculate the memory usage of the hash table.
|
// calculate the memory usage of the hash table.
|
||||||
size := len(memoryHashMap)
|
size := len(memoryHashMap)
|
||||||
|
muForMemoryHash.RUnlock()
|
||||||
|
|
||||||
// If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared.
|
// If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared.
|
||||||
if size > limit {
|
if size > limit {
|
||||||
|
muForMemoryHash.Lock()
|
||||||
var minKey string
|
var minKey string
|
||||||
var minVal int
|
var minVal int
|
||||||
for k, v := range memoryHashCounter {
|
for k, v := range memoryHashCounter {
|
||||||
@@ -1204,6 +1229,7 @@ func IndexOf[T comparable](arr []T, val T) int {
|
|||||||
}
|
}
|
||||||
delete(memoryHashMap, minKey)
|
delete(memoryHashMap, minKey)
|
||||||
delete(memoryHashCounter, minKey)
|
delete(memoryHashCounter, minKey)
|
||||||
|
muForMemoryHash.Unlock()
|
||||||
}
|
}
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool,
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// UniqueByParallel removes duplicate elements from the slice by parallel
|
// UniqueByConcurrent removes duplicate elements from the slice by parallel
|
||||||
// The comparator function is used to compare the elements
|
// The comparator function is used to compare the elements
|
||||||
// The numThreads parameter specifies the number of threads to use
|
// The numThreads parameter specifies the number of threads to use
|
||||||
// If numThreads is less than or equal to 0, it will be set to 1
|
// If numThreads is less than or equal to 0, it will be set to 1
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -1367,6 +1368,37 @@ func TestIndexOf(t *testing.T) {
|
|||||||
assert.Equal(-1, IndexOf(arr3, "r"))
|
assert.Equal(-1, IndexOf(arr3, "r"))
|
||||||
assert.Equal(2, memoryHashCounter[key3])
|
assert.Equal(2, memoryHashCounter[key3])
|
||||||
assert.Equal(0, memoryHashCounter[minKey])
|
assert.Equal(0, memoryHashCounter[minKey])
|
||||||
|
|
||||||
|
arr4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
|
||||||
|
const numGoroutines = 100
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(numGoroutines)
|
||||||
|
|
||||||
|
for i := 0; i < numGoroutines; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
defer wg.Done()
|
||||||
|
index := IndexOf(arr4, i%10+1)
|
||||||
|
assert.Equal(i%10, index)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexOfDifferentSizes(b *testing.B) {
|
||||||
|
sizes := []int{10, 100, 1000, 10000, 100000}
|
||||||
|
for _, size := range sizes {
|
||||||
|
arr := make([]int, size)
|
||||||
|
for i := 0; i < len(arr); i++ {
|
||||||
|
arr[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_ = IndexOf(arr, size/2) // 查找数组中间的元素
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLastIndexOf(t *testing.T) {
|
func TestLastIndexOf(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user