Skip to content

Commit 4141a17

Browse files
v1/ast: Add benchmarks for Array lazy hash computation
Add comprehensive benchmarks to measure the impact of lazy hash computation optimization for Array operations.
1 parent 7ce4593 commit 4141a17

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

v1/ast/array_hash_bench_test.go

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// Copyright 2024 The OPA Authors. All rights reserved.
2+
// Use of this source code is governed by an Apache2
3+
// license that can be found in the LICENSE file.
4+
5+
package ast
6+
7+
import (
8+
"strconv"
9+
"testing"
10+
)
11+
12+
// BenchmarkArrayCreation benchmarks array creation with different sizes.
13+
// This measures the impact of lazy hash computation vs eager hash computation.
14+
func BenchmarkArrayCreation(b *testing.B) {
15+
sizes := []int{10, 100, 1000, 10000}
16+
for _, n := range sizes {
17+
b.Run(strconv.Itoa(n), func(b *testing.B) {
18+
terms := make([]*Term, n)
19+
for i := range n {
20+
terms[i] = IntNumberTerm(i)
21+
}
22+
b.ResetTimer()
23+
b.ReportAllocs()
24+
for b.Loop() {
25+
_ = NewArray(terms...)
26+
}
27+
})
28+
}
29+
}
30+
31+
// BenchmarkArrayHash benchmarks hash computation on arrays.
32+
// With lazy evaluation, first hash access triggers computation.
33+
func BenchmarkArrayHash(b *testing.B) {
34+
sizes := []int{10, 100, 1000, 10000}
35+
for _, n := range sizes {
36+
b.Run(strconv.Itoa(n), func(b *testing.B) {
37+
terms := make([]*Term, n)
38+
for i := range n {
39+
terms[i] = IntNumberTerm(i)
40+
}
41+
arr := NewArray(terms...)
42+
b.ResetTimer()
43+
b.ReportAllocs()
44+
for b.Loop() {
45+
_ = arr.Hash()
46+
}
47+
})
48+
}
49+
}
50+
51+
// BenchmarkArrayHashRepeated benchmarks repeated hash access.
52+
// With lazy evaluation and caching, subsequent accesses should be faster.
53+
func BenchmarkArrayHashRepeated(b *testing.B) {
54+
sizes := []int{10, 100, 1000, 10000}
55+
for _, n := range sizes {
56+
b.Run(strconv.Itoa(n), func(b *testing.B) {
57+
terms := make([]*Term, n)
58+
for i := range n {
59+
terms[i] = IntNumberTerm(i)
60+
}
61+
arr := NewArray(terms...)
62+
// Trigger hash computation once
63+
_ = arr.Hash()
64+
b.ResetTimer()
65+
b.ReportAllocs()
66+
for b.Loop() {
67+
_ = arr.Hash()
68+
}
69+
})
70+
}
71+
}
72+
73+
// BenchmarkArrayAppend benchmarks appending elements to arrays.
74+
// This measures the impact of incremental hash updates.
75+
func BenchmarkArrayAppend(b *testing.B) {
76+
sizes := []int{10, 100, 1000}
77+
for _, n := range sizes {
78+
b.Run(strconv.Itoa(n), func(b *testing.B) {
79+
terms := make([]*Term, n)
80+
for i := range n {
81+
terms[i] = IntNumberTerm(i)
82+
}
83+
newTerm := IntNumberTerm(9999)
84+
b.ResetTimer()
85+
b.ReportAllocs()
86+
for b.Loop() {
87+
arr := NewArray(terms...)
88+
_ = arr.Append(newTerm)
89+
}
90+
})
91+
}
92+
}
93+
94+
// BenchmarkArrayAppendWithHash benchmarks appending when hash is already computed.
95+
// This tests the incremental hash update optimization.
96+
func BenchmarkArrayAppendWithHash(b *testing.B) {
97+
sizes := []int{10, 100, 1000}
98+
for _, n := range sizes {
99+
b.Run(strconv.Itoa(n), func(b *testing.B) {
100+
terms := make([]*Term, n)
101+
for i := range n {
102+
terms[i] = IntNumberTerm(i)
103+
}
104+
newTerm := IntNumberTerm(9999)
105+
b.ResetTimer()
106+
b.ReportAllocs()
107+
for b.Loop() {
108+
arr := NewArray(terms...)
109+
_ = arr.Hash() // Force hash computation
110+
_ = arr.Append(newTerm)
111+
}
112+
})
113+
}
114+
}
115+
116+
// BenchmarkArrayCopy benchmarks copying arrays.
117+
// This measures memory allocation savings from not copying hashs slice.
118+
func BenchmarkArrayCopy(b *testing.B) {
119+
sizes := []int{10, 100, 1000, 10000}
120+
for _, n := range sizes {
121+
b.Run(strconv.Itoa(n), func(b *testing.B) {
122+
terms := make([]*Term, n)
123+
for i := range n {
124+
terms[i] = IntNumberTerm(i)
125+
}
126+
arr := NewArray(terms...)
127+
b.ResetTimer()
128+
b.ReportAllocs()
129+
for b.Loop() {
130+
_ = arr.Copy()
131+
}
132+
})
133+
}
134+
}
135+
136+
// BenchmarkArraySlice benchmarks slicing arrays.
137+
// This measures the impact of not copying hashs slice during slicing.
138+
func BenchmarkArraySlice(b *testing.B) {
139+
sizes := []int{100, 1000, 10000}
140+
for _, n := range sizes {
141+
b.Run(strconv.Itoa(n), func(b *testing.B) {
142+
terms := make([]*Term, n)
143+
for i := range n {
144+
terms[i] = IntNumberTerm(i)
145+
}
146+
arr := NewArray(terms...)
147+
b.ResetTimer()
148+
b.ReportAllocs()
149+
for b.Loop() {
150+
_ = arr.Slice(0, n/2)
151+
}
152+
})
153+
}
154+
}
155+
156+
// BenchmarkArraySet benchmarks setting array elements.
157+
// This tests hash invalidation performance.
158+
func BenchmarkArraySet(b *testing.B) {
159+
sizes := []int{10, 100, 1000}
160+
for _, n := range sizes {
161+
b.Run(strconv.Itoa(n), func(b *testing.B) {
162+
terms := make([]*Term, n)
163+
for i := range n {
164+
terms[i] = IntNumberTerm(i)
165+
}
166+
arr := NewArray(terms...)
167+
newTerm := IntNumberTerm(9999)
168+
b.ResetTimer()
169+
b.ReportAllocs()
170+
for b.Loop() {
171+
arr.Set(0, newTerm)
172+
}
173+
})
174+
}
175+
}
176+
177+
// BenchmarkArraySorted benchmarks sorting arrays.
178+
// With lazy evaluation, sorted arrays can preserve computed hash.
179+
func BenchmarkArraySorted(b *testing.B) {
180+
sizes := []int{10, 100, 1000}
181+
for _, n := range sizes {
182+
b.Run(strconv.Itoa(n), func(b *testing.B) {
183+
terms := make([]*Term, n)
184+
for i := range n {
185+
terms[i] = IntNumberTerm(n - i) // reverse order
186+
}
187+
arr := NewArray(terms...)
188+
// Trigger hash computation
189+
_ = arr.Hash()
190+
b.ResetTimer()
191+
b.ReportAllocs()
192+
for b.Loop() {
193+
_ = arr.Sorted()
194+
}
195+
})
196+
}
197+
}
198+
199+
// BenchmarkArrayNoHashAccess benchmarks operations that don't access hash.
200+
// This shows the benefit of lazy evaluation when hash is never needed.
201+
func BenchmarkArrayNoHashAccess(b *testing.B) {
202+
sizes := []int{10, 100, 1000, 10000}
203+
for _, n := range sizes {
204+
b.Run(strconv.Itoa(n), func(b *testing.B) {
205+
terms := make([]*Term, n)
206+
for i := range n {
207+
terms[i] = IntNumberTerm(i)
208+
}
209+
b.ResetTimer()
210+
b.ReportAllocs()
211+
for b.Loop() {
212+
arr := NewArray(terms...)
213+
_ = arr.Len()
214+
_ = arr.Elem(0)
215+
}
216+
})
217+
}
218+
}

0 commit comments

Comments
 (0)