xref: /aosp_15_r20/external/starlark-go/starlark/hashtable_test.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faust// Copyright 2017 The Bazel Authors. All rights reserved.
2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style
3*4947cdc7SCole Faust// license that can be found in the LICENSE file.
4*4947cdc7SCole Faust
5*4947cdc7SCole Faustpackage starlark
6*4947cdc7SCole Faust
7*4947cdc7SCole Faustimport (
8*4947cdc7SCole Faust	"fmt"
9*4947cdc7SCole Faust	"math/rand"
10*4947cdc7SCole Faust	"sync"
11*4947cdc7SCole Faust	"testing"
12*4947cdc7SCole Faust)
13*4947cdc7SCole Faust
14*4947cdc7SCole Faustfunc TestHashtable(t *testing.T) {
15*4947cdc7SCole Faust	makeTestIntsOnce.Do(makeTestInts)
16*4947cdc7SCole Faust	testHashtable(t, make(map[int]bool))
17*4947cdc7SCole Faust}
18*4947cdc7SCole Faust
19*4947cdc7SCole Faustfunc BenchmarkStringHash(b *testing.B) {
20*4947cdc7SCole Faust	for len := 1; len <= 1024; len *= 2 {
21*4947cdc7SCole Faust		buf := make([]byte, len)
22*4947cdc7SCole Faust		rand.New(rand.NewSource(0)).Read(buf)
23*4947cdc7SCole Faust		s := string(buf)
24*4947cdc7SCole Faust
25*4947cdc7SCole Faust		b.Run(fmt.Sprintf("hard-%d", len), func(b *testing.B) {
26*4947cdc7SCole Faust			for i := 0; i < b.N; i++ {
27*4947cdc7SCole Faust				hashString(s)
28*4947cdc7SCole Faust			}
29*4947cdc7SCole Faust		})
30*4947cdc7SCole Faust		b.Run(fmt.Sprintf("soft-%d", len), func(b *testing.B) {
31*4947cdc7SCole Faust			for i := 0; i < b.N; i++ {
32*4947cdc7SCole Faust				softHashString(s)
33*4947cdc7SCole Faust			}
34*4947cdc7SCole Faust		})
35*4947cdc7SCole Faust	}
36*4947cdc7SCole Faust}
37*4947cdc7SCole Faust
38*4947cdc7SCole Faustfunc BenchmarkHashtable(b *testing.B) {
39*4947cdc7SCole Faust	makeTestIntsOnce.Do(makeTestInts)
40*4947cdc7SCole Faust	b.ResetTimer()
41*4947cdc7SCole Faust	for i := 0; i < b.N; i++ {
42*4947cdc7SCole Faust		testHashtable(b, nil)
43*4947cdc7SCole Faust	}
44*4947cdc7SCole Faust}
45*4947cdc7SCole Faust
46*4947cdc7SCole Faustconst testIters = 10000
47*4947cdc7SCole Faust
48*4947cdc7SCole Faustvar (
49*4947cdc7SCole Faust	// testInts is a zipf-distributed array of Ints and corresponding ints.
50*4947cdc7SCole Faust	// This removes the cost of generating them on the fly during benchmarking.
51*4947cdc7SCole Faust	// Without this, Zipf and MakeInt dominate CPU and memory costs, respectively.
52*4947cdc7SCole Faust	makeTestIntsOnce sync.Once
53*4947cdc7SCole Faust	testInts         [3 * testIters]struct {
54*4947cdc7SCole Faust		Int   Int
55*4947cdc7SCole Faust		goInt int
56*4947cdc7SCole Faust	}
57*4947cdc7SCole Faust)
58*4947cdc7SCole Faust
59*4947cdc7SCole Faustfunc makeTestInts() {
60*4947cdc7SCole Faust	zipf := rand.NewZipf(rand.New(rand.NewSource(0)), 1.1, 1.0, 1000.0)
61*4947cdc7SCole Faust	for i := range &testInts {
62*4947cdc7SCole Faust		r := int(zipf.Uint64())
63*4947cdc7SCole Faust		testInts[i].goInt = r
64*4947cdc7SCole Faust		testInts[i].Int = MakeInt(r)
65*4947cdc7SCole Faust	}
66*4947cdc7SCole Faust}
67*4947cdc7SCole Faust
68*4947cdc7SCole Faust// testHashtable is both a test and a benchmark of hashtable.
69*4947cdc7SCole Faust// When sane != nil, it acts as a test against the semantics of Go's map.
70*4947cdc7SCole Faustfunc testHashtable(tb testing.TB, sane map[int]bool) {
71*4947cdc7SCole Faust	var i int // index into testInts
72*4947cdc7SCole Faust
73*4947cdc7SCole Faust	var ht hashtable
74*4947cdc7SCole Faust
75*4947cdc7SCole Faust	// Insert 10000 random ints into the map.
76*4947cdc7SCole Faust	for j := 0; j < testIters; j++ {
77*4947cdc7SCole Faust		k := testInts[i]
78*4947cdc7SCole Faust		i++
79*4947cdc7SCole Faust		if err := ht.insert(k.Int, None); err != nil {
80*4947cdc7SCole Faust			tb.Fatal(err)
81*4947cdc7SCole Faust		}
82*4947cdc7SCole Faust		if sane != nil {
83*4947cdc7SCole Faust			sane[k.goInt] = true
84*4947cdc7SCole Faust		}
85*4947cdc7SCole Faust	}
86*4947cdc7SCole Faust
87*4947cdc7SCole Faust	// Do 10000 random lookups in the map.
88*4947cdc7SCole Faust	for j := 0; j < testIters; j++ {
89*4947cdc7SCole Faust		k := testInts[i]
90*4947cdc7SCole Faust		i++
91*4947cdc7SCole Faust		_, found, err := ht.lookup(k.Int)
92*4947cdc7SCole Faust		if err != nil {
93*4947cdc7SCole Faust			tb.Fatal(err)
94*4947cdc7SCole Faust		}
95*4947cdc7SCole Faust		if sane != nil {
96*4947cdc7SCole Faust			_, found2 := sane[k.goInt]
97*4947cdc7SCole Faust			if found != found2 {
98*4947cdc7SCole Faust				tb.Fatal("sanity check failed")
99*4947cdc7SCole Faust			}
100*4947cdc7SCole Faust		}
101*4947cdc7SCole Faust	}
102*4947cdc7SCole Faust
103*4947cdc7SCole Faust	// Do 10000 random deletes from the map.
104*4947cdc7SCole Faust	for j := 0; j < testIters; j++ {
105*4947cdc7SCole Faust		k := testInts[i]
106*4947cdc7SCole Faust		i++
107*4947cdc7SCole Faust		_, found, err := ht.delete(k.Int)
108*4947cdc7SCole Faust		if err != nil {
109*4947cdc7SCole Faust			tb.Fatal(err)
110*4947cdc7SCole Faust		}
111*4947cdc7SCole Faust		if sane != nil {
112*4947cdc7SCole Faust			_, found2 := sane[k.goInt]
113*4947cdc7SCole Faust			if found != found2 {
114*4947cdc7SCole Faust				tb.Fatal("sanity check failed")
115*4947cdc7SCole Faust			}
116*4947cdc7SCole Faust			delete(sane, k.goInt)
117*4947cdc7SCole Faust		}
118*4947cdc7SCole Faust	}
119*4947cdc7SCole Faust
120*4947cdc7SCole Faust	if sane != nil {
121*4947cdc7SCole Faust		if int(ht.len) != len(sane) {
122*4947cdc7SCole Faust			tb.Fatal("sanity check failed")
123*4947cdc7SCole Faust		}
124*4947cdc7SCole Faust	}
125*4947cdc7SCole Faust}
126