tile38/vendor/github.com/tidwall/rhh/u64_test.go

299 lines
6.7 KiB
Go

// Copyright 2019 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an ISC-style
// license that can be found in the LICENSE file.
package rhh
import (
"fmt"
"math/rand"
"os"
"runtime"
"strconv"
"testing"
"time"
"github.com/tidwall/lotsa"
)
type keyTU64 = uint64
type valueTU64 = interface{}
func kU64(key int) keyTU64 {
return uint64(key)
}
func addU64(x keyTU64, delta int) int {
return int(x) + delta
}
///////////////////////////
func randomU64(N int, perm bool) []keyTU64 {
nums := make([]keyTU64, N)
if perm {
for i, x := range rand.Perm(N) {
nums[i] = kU64(x)
}
} else {
m := make(map[keyTU64]bool)
for len(m) < N {
m[kU64(int(rand.Uint64()))] = true
}
var i int
for k := range m {
nums[i] = k
i++
}
}
return nums
}
func shuffleU64(nums []keyTU64) {
for i := range nums {
j := rand.Intn(i + 1)
nums[i], nums[j] = nums[j], nums[i]
}
}
func init() {
//var seed int64 = 1519776033517775607
seed := (time.Now().UnixNano())
println("seed:", seed)
rand.Seed(seed)
}
func TestRandomDataU64(t *testing.T) {
N := 10000
start := time.Now()
for time.Since(start) < time.Second*2 {
nums := randomU64(N, true)
var m *MapU64
switch rand.Int() % 5 {
default:
m = NewU64(N / ((rand.Int() % 3) + 1))
case 1:
m = new(MapU64)
case 2:
m = NewU64(0)
}
v, ok := m.Get(kU64(999))
if ok || v != nil {
t.Fatalf("expected %v, got %v", nil, v)
}
v, ok = m.Delete(kU64(999))
if ok || v != nil {
t.Fatalf("expected %v, got %v", nil, v)
}
if m.Len() != 0 {
t.Fatalf("expected %v, got %v", 0, m.Len())
}
// set a bunch of items
for i := 0; i < len(nums); i++ {
v, ok := m.Set(nums[i], nums[i])
if ok || v != nil {
t.Fatalf("expected %v, got %v", nil, v)
}
}
if m.Len() != N {
t.Fatalf("expected %v, got %v", N, m.Len())
}
// retrieve all the items
shuffleU64(nums)
for i := 0; i < len(nums); i++ {
v, ok := m.Get(nums[i])
if !ok || v == nil || v != nums[i] {
t.Fatalf("expected %v, got %v", nums[i], v)
}
}
// replace all the items
shuffleU64(nums)
for i := 0; i < len(nums); i++ {
v, ok := m.Set(nums[i], addU64(nums[i], 1))
if !ok || v != nums[i] {
t.Fatalf("expected %v, got %v", nums[i], v)
}
}
if m.Len() != N {
t.Fatalf("expected %v, got %v", N, m.Len())
}
// retrieve all the items
shuffleU64(nums)
for i := 0; i < len(nums); i++ {
v, ok := m.Get(nums[i])
if !ok || v != addU64(nums[i], 1) {
t.Fatalf("expected %v, got %v", addU64(nums[i], 1), v)
}
}
// remove half the items
shuffleU64(nums)
for i := 0; i < len(nums)/2; i++ {
v, ok := m.Delete(nums[i])
if !ok || v != addU64(nums[i], 1) {
t.Fatalf("expected %v, got %v", addU64(nums[i], 1), v)
}
}
if m.Len() != N/2 {
t.Fatalf("expected %v, got %v", N/2, m.Len())
}
// check to make sure that the items have been removed
for i := 0; i < len(nums)/2; i++ {
v, ok := m.Get(nums[i])
if ok || v != nil {
t.Fatalf("expected %v, got %v", nil, v)
}
}
// check the second half of the items
for i := len(nums) / 2; i < len(nums); i++ {
v, ok := m.Get(nums[i])
if !ok || v != addU64(nums[i], 1) {
t.Fatalf("expected %v, got %v", addU64(nums[i], 1), v)
}
}
// try to delete again, make sure they don't exist
for i := 0; i < len(nums)/2; i++ {
v, ok := m.Delete(nums[i])
if ok || v != nil {
t.Fatalf("expected %v, got %v", nil, v)
}
}
if m.Len() != N/2 {
t.Fatalf("expected %v, got %v", N/2, m.Len())
}
m.Range(func(key keyTU64, value valueTU64) bool {
if value != addU64(key, 1) {
t.Fatalf("expected %v, got %v", addU64(key, 1), value)
}
return true
})
var n int
m.Range(func(key keyTU64, value valueTU64) bool {
n++
return false
})
if n != 1 {
t.Fatalf("expected %v, got %v", 1, n)
}
for i := len(nums) / 2; i < len(nums); i++ {
v, ok := m.Delete(nums[i])
if !ok || v != addU64(nums[i], 1) {
t.Fatalf("expected %v, got %v", addU64(nums[i], 1), v)
}
}
}
}
func TestBenchU64(t *testing.T) {
N, _ := strconv.ParseUint(os.Getenv("MAPBENCH"), 10, 64)
if N == 0 {
fmt.Printf("Enable benchmarks with MAPBENCH=1000000\n")
return
}
nums := randomU64(int(N), false)
var pnums []valueTU64
for i := range nums {
pnums = append(pnums, valueTU64(&nums[i]))
}
fmt.Printf("\n## UINT64 KEYS\n\n")
t.Run("RobinHood", func(t *testing.T) {
testPerfU64(nums, pnums, "robinhood")
})
t.Run("Stdlib", func(t *testing.T) {
testPerfU64(nums, pnums, "stdlib")
})
}
func testPerfU64(nums []keyTU64, pnums []valueTU64, which string) {
var ms1, ms2 runtime.MemStats
initSize := 0 //len(nums) * 2
defer func() {
heapBytes := int(ms2.HeapAlloc - ms1.HeapAlloc)
fmt.Printf("memory %13s bytes %19s/entry \n",
commaize(heapBytes), commaize(heapBytes/len(nums)))
fmt.Printf("\n")
}()
runtime.GC()
time.Sleep(time.Millisecond * 100)
runtime.ReadMemStats(&ms1)
var setop, getop, delop func(int, int)
var scnop func()
switch which {
case "stdlib":
m := make(map[keyTU64]valueTU64, initSize)
setop = func(i, _ int) { m[nums[i]] = pnums[i] }
getop = func(i, _ int) { _ = m[nums[i]] }
delop = func(i, _ int) { delete(m, nums[i]) }
scnop = func() {
for range m {
}
}
case "robinhood":
m := NewU64(initSize)
setop = func(i, _ int) { m.Set(nums[i], pnums[i]) }
getop = func(i, _ int) { m.Get(nums[i]) }
delop = func(i, _ int) { m.Delete(nums[i]) }
scnop = func() {
m.Range(func(key keyTU64, value valueTU64) bool {
return true
})
}
}
fmt.Printf("-- %s --", which)
fmt.Printf("\n")
ops := []func(int, int){setop, getop, setop, nil, delop}
tags := []string{"set", "get", "reset", "scan", "delete"}
for i := range ops {
shuffleU64(nums)
var na bool
var n int
start := time.Now()
if tags[i] == "scan" {
op := scnop
if op == nil {
na = true
} else {
n = 20
lotsa.Ops(n, 1, func(_, _ int) { op() })
}
} else {
n = len(nums)
lotsa.Ops(n, 1, ops[i])
}
dur := time.Since(start)
if i == 0 {
runtime.GC()
time.Sleep(time.Millisecond * 100)
runtime.ReadMemStats(&ms2)
}
printItem(tags[i], 9, -1)
if na {
printItem("-- unavailable --", 14, 1)
} else {
if n == -1 {
printItem("unknown ops", 14, 1)
} else {
printItem(fmt.Sprintf("%s ops", commaize(n)), 14, 1)
}
printItem(fmt.Sprintf("%.0fms", dur.Seconds()*1000), 8, 1)
if n != -1 {
printItem(fmt.Sprintf("%s/sec", commaize(int(float64(n)/dur.Seconds()))), 18, 1)
}
}
fmt.Printf("\n")
}
}
func TestHashDIBU64(t *testing.T) {
var e entryU64
e.setDIB(100)
e.setHash(90000)
if e.dib() != 100 {
t.Fatalf("expected %v, got %v", 100, e.dib())
}
if e.hash() != 90000 {
t.Fatalf("expected %v, got %v", 90000, e.hash())
}
}