diff --git a/pkg/infoschema/sieve.go b/pkg/infoschema/sieve.go index f61e776b47..761a6d3472 100644 --- a/pkg/infoschema/sieve.go +++ b/pkg/infoschema/sieve.go @@ -18,6 +18,7 @@ import ( "container/list" "context" "sync" + "sync/atomic" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/pkg/infoschema/internal" @@ -27,7 +28,7 @@ import ( type entry[K comparable, V any] struct { key K value V - visited bool + visited atomic.Bool element *list.Element size uint64 } @@ -48,7 +49,7 @@ func (t *entry[K, V]) Size() uint64 { type Sieve[K comparable, V any] struct { ctx context.Context cancel context.CancelFunc - mu sync.Mutex + mu sync.RWMutex size uint64 // capacity can be set to zero for disabling infoschema v2 capacity uint64 @@ -132,7 +133,7 @@ func (s *Sieve[K, V]) Set(key K, value V) { if e, ok := s.items[key]; ok { e.value = value - e.visited = true + e.visited.Store(true) return } @@ -156,10 +157,13 @@ func (s *Sieve[K, V]) Get(key K) (value V, ok bool) { var v V failpoint.Return(v, false) }) - s.mu.Lock() - defer s.mu.Unlock() - if e, ok := s.items[key]; ok { - e.visited = true + + s.mu.RLock() + e, ok := s.items[key] + s.mu.RUnlock() + + if ok { + e.visited.Store(true) s.hook.onHit() return e.value, true } @@ -254,8 +258,8 @@ func (s *Sieve[K, V]) evict() { panic("sieve: evicting non-existent element") } - for el.visited { - el.visited = false + for el.visited.Load() { + el.visited.Store(false) o = o.Prev() if o == nil { o = s.ll.Back() diff --git a/pkg/infoschema/sieve_test.go b/pkg/infoschema/sieve_test.go index ef2e67c2f8..5109877deb 100644 --- a/pkg/infoschema/sieve_test.go +++ b/pkg/infoschema/sieve_test.go @@ -15,6 +15,9 @@ package infoschema import ( + "context" + "math/rand" + "sync/atomic" "testing" "github.com/pingcap/tidb/pkg/util/size" @@ -135,3 +138,43 @@ func TestPurge(t *testing.T) { cache.Close() } + +func BenchmarkSieveGet(b *testing.B) { + c := newSieve[int, int](8192) + ent := make([]int, b.N) + for i := 0; i < b.N; i++ { + var k int + if i%2 == 0 { + k = int(rand.Int63() % 16384) + } else { + k = int(rand.Int63() % 32768) + } + c.Set(k, k) + ent[i] = k + } + + ctx, cancel := context.WithCancel(context.Background()) + go func() { + for { + v := int(rand.Int63() % 8192) + c.Set(v, v) + if ctx.Err() != nil { + return + } + } + }() + + b.ResetTimer() + + var hit, miss int64 + for i := 0; i < b.N; i++ { + if _, ok := c.Get(ent[i]); ok { + atomic.AddInt64(&hit, 1) + } else { + atomic.AddInt64(&miss, 1) + } + } + + b.Logf("%d: hit %d, miss %d, ratio %4.2f", b.N, hit, miss, float64(hit)/float64(hit+miss)) + cancel() +}