Files
tidb/pkg/util/hack/map_abi_test.go
2025-12-10 09:40:20 +00:00

238 lines
5.6 KiB
Go

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hack
import (
"fmt"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
const seed = 4992862800126241206 // set the fixed seed for test
func TestSwissTable(t *testing.T) {
require.True(t, maxTableCapacity == 1024)
{
m := make(map[int]int)
tp := ToSwissMap(m).Type
require.True(t, tp.GroupSize == 136)
require.True(t, tp.SlotSize == 16)
require.True(t, tp.ElemOff == 8)
}
{
m := make(map[int32]int32)
tp := ToSwissMap(m).Type
require.True(t, tp.GroupSize == 72)
require.True(t, tp.SlotSize == 8)
require.True(t, tp.ElemOff == 4)
}
{
m := make(map[int8]int8)
tp := ToSwissMap(m).Type
require.True(t, tp.GroupSize == 24)
require.True(t, tp.SlotSize == 2)
require.True(t, tp.ElemOff == 1)
}
{
m := make(map[int64]float64)
tp := ToSwissMap(m).Type
require.True(t, tp.GroupSize == 136)
require.True(t, tp.SlotSize == 16)
require.True(t, tp.ElemOff == 8)
}
{
m := make(map[complex128]complex128)
tp := ToSwissMap(m).Type
require.True(t, tp.GroupSize == 264)
require.True(t, tp.SlotSize == 32)
require.True(t, tp.ElemOff == 16)
}
{
const N = 1024
mp := make(map[uint64]uint64)
sm := ToSwissMap(mp)
sm.Data.MockSeedForTest(seed)
mp[1234] = 5678
for i := range N {
mp[uint64(i)] = uint64(i * 2)
}
require.Equal(t, N+1, len(mp))
require.Equal(t, uint64(N+1), sm.Data.Used)
found := false
var lastTab *swissMapTable
for i := range sm.Data.dirLen {
table := sm.Data.directoryAt(uintptr(i))
if table == lastTab {
continue
}
for i := range table.groups.lengthMask + 1 {
ref := table.groups.group(sm.Type, i)
require.True(t, (sm.Type.GroupSize-groupSlotsOffset)%sm.Type.SlotSize == 0)
capacity := ref.cap(sm.Type)
require.True(t, capacity == swissMapGroupSlots)
for j := range capacity {
k, v := *(*uint64)(ref.key(sm.Type, uintptr(j))), *(*uint64)(ref.elem(sm.Type, uintptr(j)))
if k == 1234 && v == 5678 {
require.False(t, found)
found = true
break
}
}
}
}
require.True(t, found)
oriSeed := sm.Data.seed
for k := range mp {
delete(mp, k)
}
require.True(t, oriSeed != sm.Data.seed)
}
{
const N = 2000
mp := make(map[string]int)
sm := ToSwissMap(mp)
sm.Data.MockSeedForTest(seed)
for i := range N {
mp[fmt.Sprintf("key-%d", i)] = i
}
require.Equal(t, N, len(mp))
require.Equal(t, N, int(sm.Data.Used))
require.True(t, sm.Type.GroupSize == 200)
require.True(t, sm.Data.dirLen == 4)
require.Equal(t, 102608, int(sm.Size()))
}
{
mp := make(map[int]int)
require.Equal(t, 0, len(mp))
sm := ToSwissMap(mp)
sm.Data.MockSeedForTest(seed)
require.Equal(t, 0, int(sm.Data.Used))
require.True(t, sm.Type.GroupSize == 136)
require.Equal(t, 184, int(sm.Size()))
for i := range 8 {
mp[i] = i
}
require.Equal(t, 8, len(mp))
require.Equal(t, 184, int(sm.Size()))
mp[9] = 9
require.Equal(t, 9, len(mp))
require.Equal(t, 360, int(sm.Size()))
}
{
mp := make(map[complex128]complex128)
m := MemAwareMap[complex128, complex128]{}
const N = 1024*50 - 1
delta := m.Init(mp)
m.MockSeedForTest(seed)
for i := range N {
k := complex(float64(i), float64(i))
d := m.Set(k, k)
delta += d
if d > 0 {
sz := m.RealBytes()
expMin := sz * 75 / 100
require.True(t, m.Bytes >= expMin, "ApproxSize %d, RealSize %d, index %d, expMin %d", m.Bytes, sz, i, expMin)
require.True(t, approxSize(m.groupSize, uint64(m.Len())) >= expMin, "ApproxSize %d, RealSize %d, index %d, expMin %d", m.Bytes, sz, i, expMin)
}
}
sz := m.RealBytes()
require.True(t, sz == 2165296, sz)
require.True(t, delta == 2702278, delta)
require.True(t, delta == int64(m.Bytes))
require.True(t, seed == m.unwrap().seed)
clearSeq := m.unwrap().clearSeq
clear(m.M)
require.True(t, m.Len() == 0)
require.True(t, clearSeq+1 == m.unwrap().clearSeq)
require.True(t, m.unwrap().seed != seed)
require.True(t, sz == m.RealBytes())
require.True(t, delta == int64(m.Bytes))
m.MockSeedForTest(seed)
for i := range 1024 {
k := complex(float64(i), float64(i))
d, insert := m.SetExt(k, k)
require.True(t, d == 0)
require.True(t, insert)
}
require.True(t, m.Len() == 1024)
}
}
var result int
var inputs = []struct {
input int
}{
{input: 1},
{input: 100},
{input: 10000},
{input: 1000000},
}
func memAwareIntMap(size int) int {
var x int
m := NewMemAwareMap[int, int](0)
for j := range size {
m.Set(j, j)
}
for j := range size {
x, _ = m.Get(j)
}
return x
}
func nativeIntMap(size int) int {
var x int
m := make(map[int]int)
for j := range size {
m[j] = j
}
for j := range size {
x = m[j]
}
return x
}
func BenchmarkMemAwareIntMap(b *testing.B) {
for _, s := range inputs {
b.Run("MemAwareIntMap_"+strconv.Itoa(s.input), func(b *testing.B) {
var x int
for b.Loop() {
x = memAwareIntMap(s.input)
}
result = x
})
}
}
func BenchmarkNativeIntMap(b *testing.B) {
for _, s := range inputs {
b.Run("NativeIntMap_"+strconv.Itoa(s.input), func(b *testing.B) {
var x int
for b.Loop() {
x = nativeIntMap(s.input)
}
result = x
})
}
}