Skip to content

Commit ae2f344

Browse files
committed
Update benchmarks further
1 parent 85f1f05 commit ae2f344

File tree

2 files changed

+112
-96
lines changed

2 files changed

+112
-96
lines changed

hamt_bench_test.go

Lines changed: 110 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ type rander struct {
1515
r *rand.Rand
1616
}
1717

18-
func (r *rander) randString() string {
19-
buf := make([]byte, 18)
18+
func (r *rander) randString(stringSize int) string {
19+
buf := make([]byte, stringSize)
2020
rand.Read(buf)
2121
return hex.EncodeToString(buf)
2222
}
2323

24-
func (r *rander) randValue() []byte {
25-
buf := make([]byte, 30)
24+
func (r *rander) randValue(datasize int) []byte {
25+
buf := make([]byte, datasize)
2626
rand.Read(buf)
2727
return buf
2828
}
@@ -34,7 +34,7 @@ func BenchmarkSerializeNode(b *testing.B) {
3434
n := NewNode(cs)
3535

3636
for i := 0; i < 50; i++ {
37-
if err := n.Set(context.TODO(), r.randString(), r.randValue()); err != nil {
37+
if err := n.Set(context.TODO(), r.randString(18), r.randValue(30)); err != nil {
3838
b.Fatal(err)
3939
}
4040
}
@@ -50,83 +50,92 @@ func BenchmarkSerializeNode(b *testing.B) {
5050
}
5151
}
5252

53-
type benchSetCase struct {
54-
kcount int
55-
bitwidth int
56-
datasize int
57-
flushInterval int
58-
}
59-
60-
type benchFillCase struct {
61-
kcount int
62-
bitwidth int
63-
datasize int
64-
flushInterval int
53+
type hamtParams struct {
54+
id string
55+
count int
56+
datasize int
57+
keysize int
6558
}
6659

67-
type benchFindCase struct {
68-
kcount int
60+
type benchCase struct {
61+
id string
62+
count int
6963
bitwidth int
7064
datasize int
65+
keysize int
7166
}
7267

73-
var benchSetCaseTable []benchSetCase
74-
var benchFillCaseTable []benchFillCase
75-
var benchFindCaseTable []benchFindCase
68+
var caseTable []benchCase
7669

7770
func init() {
78-
kCounts := []int{
79-
1,
80-
5,
81-
10,
82-
50,
83-
100,
84-
500,
85-
1000, // aka 1M
86-
//10000, // aka 10M -- you'll need a lot of RAM for this. Also, some patience.
87-
}
71+
8872
bitwidths := []int{
73+
1,
74+
2,
8975
3,
9076
4,
9177
5,
9278
6,
9379
7,
9480
8,
9581
}
96-
flushIntervals := []int{
97-
1,
98-
1000,
99-
}
100-
dataSize := []int{
101-
1,
82+
83+
hamts := []hamtParams{
84+
hamtParams{
85+
id: "init.AddressMap",
86+
count: 55649,
87+
datasize: 3,
88+
keysize: 26,
89+
},
90+
hamtParams{
91+
id: "market.PendingProposals",
92+
count: 40713,
93+
datasize: 151,
94+
keysize: 38,
95+
},
96+
hamtParams{
97+
id: "market.EscrowWTable",
98+
count: 2113,
99+
datasize: 7,
100+
keysize: 4,
101+
},
102+
hamtParams{
103+
id: "market.LockedTable",
104+
count: 2098,
105+
datasize: 4,
106+
keysize: 4,
107+
},
108+
hamtParams{
109+
id: "market.DealOpsByEpoch",
110+
count: 16558,
111+
datasize: 43,
112+
keysize: 3,
113+
},
114+
hamtParams{
115+
id: "power.CronEventQueue",
116+
count: 60,
117+
datasize: 43,
118+
keysize: 3,
119+
},
120+
hamtParams{
121+
id: "power.CLaims",
122+
count: 15610,
123+
datasize: 5,
124+
keysize: 3,
125+
},
102126
}
127+
103128
// bucketsize-aka-arraywidth? maybe someday.
104-
for _, c := range kCounts {
105-
for _, d := range dataSize {
106-
for _, bw := range bitwidths {
107-
benchFindCaseTable = append(benchFindCaseTable,
108-
benchFindCase{
109-
kcount: c,
110-
bitwidth: bw,
111-
datasize: d,
112-
})
113-
for _, f := range flushIntervals {
114-
benchFillCaseTable = append(benchFillCaseTable,
115-
benchFillCase{
116-
kcount: c,
117-
bitwidth: bw,
118-
datasize: d,
119-
flushInterval: f,
120-
})
121-
benchSetCaseTable = append(benchSetCaseTable,
122-
benchSetCase{
123-
kcount: c,
124-
bitwidth: bw,
125-
datasize: d,
126-
flushInterval: f,
127-
})
128-
}
129-
}
129+
for _, h := range hamts {
130+
for _, bw := range bitwidths {
131+
caseTable = append(caseTable,
132+
benchCase{
133+
id: fmt.Sprintf("%s -- bw=%d", h.id, bw),
134+
count: h.count,
135+
bitwidth: bw,
136+
datasize: h.datasize,
137+
keysize: h.keysize,
138+
})
130139
}
131140
}
132141
}
@@ -157,15 +166,15 @@ func init() {
157166
// See "BenchmarkSet*" for a probe of how long it takes to set additional entries in an already-large hamt
158167
// (this gives a more interesting and useful nanoseconds-per-op indicators).
159168
func BenchmarkFill(b *testing.B) {
160-
for _, t := range benchFillCaseTable {
161-
b.Run(fmt.Sprintf("n=%dk/bitwidth=%d", t.kcount, t.bitwidth), func(b *testing.B) {
169+
for _, t := range caseTable {
170+
b.Run(fmt.Sprintf("%s", t.id), func(b *testing.B) {
162171
for i := 0; i < b.N; i++ {
163172
r := rander{rand.New(rand.NewSource(int64(i)))}
164173
blockstore := newMockBlocks()
165174
n := NewNode(cbor.NewCborStore(blockstore), UseTreeBitWidth(t.bitwidth))
166175
//b.ResetTimer()
167-
for j := 0; j < t.kcount*1000; j++ {
168-
if err := n.Set(context.Background(), r.randString(), r.randValue()); err != nil {
176+
for j := 0; j < t.count; j++ {
177+
if err := n.Set(context.Background(), r.randString(t.keysize), r.randValue(t.datasize)); err != nil {
169178
b.Fatal(err)
170179
}
171180
}
@@ -179,56 +188,61 @@ func BenchmarkFill(b *testing.B) {
179188
if blockstore.stats.evtcntPutDup > 0 {
180189
b.Logf("on round N=%d: blockstore stats: %#v\n", b.N, blockstore.stats) // note: must refer to this before doing `n.checkSize`; that function has many effects.
181190
}
182-
b.ReportMetric(float64(blockstore.stats.evtcntGet)/float64(t.kcount*1000), "getEvts/entry")
183-
b.ReportMetric(float64(blockstore.stats.evtcntPut)/float64(t.kcount*1000), "putEvts/entry")
184-
b.ReportMetric(float64(len(blockstore.data))/float64(t.kcount*1000), "blocks/entry")
191+
b.ReportMetric(float64(blockstore.stats.evtcntGet)/float64(t.count), "getEvts/entry")
192+
b.ReportMetric(float64(blockstore.stats.evtcntPut)/float64(t.count), "putEvts/entry")
193+
b.ReportMetric(float64(len(blockstore.data))/float64(t.count), "blocks/entry")
185194
binarySize, _ := n.checkSize(context.Background())
186-
b.ReportMetric(float64(binarySize)/float64(t.kcount*1000), "bytes(hamtAccnt)/entry")
187-
b.ReportMetric(float64(blockstore.totalBlockSizes())/float64(t.kcount*1000), "bytes(blockstoreAccnt)/entry")
195+
b.ReportMetric(float64(binarySize)/float64(t.count), "bytes(hamtAccnt)/entry")
196+
b.ReportMetric(float64(blockstore.totalBlockSizes())/float64(t.count), "bytes(blockstoreAccnt)/entry")
188197
b.StartTimer()
189198
}
190199
})
191200
}
192201
}
193202

194-
// BenchmarkSet creates a large HAMT, then starts the timer, and does another 1000 inserts,
203+
// BenchmarkSetBulk creates a large HAMT, then starts the timer, and does another 1000 inserts,
195204
// measuring the time taken for this second batch of inserts.
196-
// Flushing rate is parameterized.
205+
// Flushing happens once after all 1000 inserts.
197206
//
198207
// The number of *additional* blocks per entry is reported.
199208
// This number is usually less than one with high flush interval means changes might be amortized.
200-
// For flush interval one this number should be at least 1 --
201-
// however, since we choose random keys, it can still turn out lower if keys happen to collide.
202-
// (The Set method does not make it possible to adjust our denominator to compensate for this: it does not yield previous values nor indicators of prior presense.)
203-
func BenchmarkSet(b *testing.B) {
204-
doBenchmarkSetSuite(b)
209+
func BenchmarkSetBulk(b *testing.B) {
210+
doBenchmarkSetSuite(b, false)
211+
}
212+
213+
// BenchmarkSetIndividual is the same as BenchmarkSetBulk, but flushes more.
214+
// Flush happens per insert.
215+
//
216+
// The number of *additional* blocks per entry is reported.
217+
// Since we flush each insert individually, this number should be at least 1.
218+
func BenchmarkSetIndividual(b *testing.B) {
219+
doBenchmarkSetSuite(b, true)
205220
}
206221

207-
func doBenchmarkSetSuite(b *testing.B) {
208-
for j, t := range benchSetCaseTable {
209-
b.Run(fmt.Sprintf("n=%dk/bitwidth=%d", t.kcount, t.bitwidth), func(b *testing.B) {
222+
func doBenchmarkSetSuite(b *testing.B, flushPer bool) {
223+
for _, t := range caseTable {
224+
b.Run(fmt.Sprintf("%s", t.id), func(b *testing.B) {
210225
for i := 0; i < b.N; i++ {
211226
b.StopTimer()
212227
r := rander{rand.New(rand.NewSource(int64(i)))}
213228
blockstore := newMockBlocks()
214229
n := NewNode(cbor.NewCborStore(blockstore), UseTreeBitWidth(t.bitwidth))
215230
// Initial fill:
216-
for j := 0; j < t.kcount*1000; j++ {
217-
if err := n.Set(context.Background(), r.randString(), r.randValue()); err != nil {
231+
for j := 0; j < t.count; j++ {
232+
if err := n.Set(context.Background(), r.randString(t.keysize), r.randValue(t.datasize)); err != nil {
218233
b.Fatal(err)
219234
}
220235
}
221236
if err := n.Flush(context.Background()); err != nil {
222237
b.Fatal(err)
223238
}
224-
initalBlockstoreSize := len(blockstore.data)
225239
// b.ResetTimer()
226240
blockstore.stats = blockstoreStats{}
227241
// Additional inserts:
228242
b.ReportAllocs()
229243
b.StartTimer()
230244
for j := 0; j < 1000; j++ {
231-
if err := n.Set(context.Background(), r.randString(), r.randValue()); err != nil {
245+
if err := n.Set(context.Background(), r.randString(t.keysize), r.randValue(t.datasize)); err != nil {
232246
b.Fatal(err)
233247
}
234248
if flushPer {
@@ -249,23 +263,23 @@ func doBenchmarkSetSuite(b *testing.B) {
249263
if blockstore.stats.evtcntPutDup > 0 {
250264
b.Logf("on round N=%d: blockstore stats: %#v\n", b.N, blockstore.stats)
251265
}
252-
b.ReportMetric(float64(blockstore.stats.evtcntGet)/float64(t.kcount*1000), "getEvts/entry")
253-
b.ReportMetric(float64(blockstore.stats.evtcntPut)/float64(t.kcount*1000), "putEvts/entry")
254-
b.ReportMetric(float64(len(blockstore.data)-initalBlockstoreSize)/float64(1000), "addntlBlocks/addntlEntry")
266+
b.ReportMetric(float64(blockstore.stats.evtcntGet)/1000, "getEvts/set")
267+
b.ReportMetric(float64(blockstore.stats.evtcntPut)/1000, "putEvts/set")
268+
b.ReportMetric(float64(blockstore.stats.bytesPut)/1000, "bytesPut/set")
255269
b.StartTimer()
256270
}
257271
})
258272
}
259273
}
260274

261275
func BenchmarkFind(b *testing.B) {
262-
for _, t := range benchSetCaseTable {
263-
b.Run(fmt.Sprintf("n=%dk/bitwidth=%d", t.kcount, t.bitwidth),
264-
doBenchmarkEntriesCount(t.kcount*1000, t.bitwidth))
276+
for _, t := range caseTable {
277+
b.Run(fmt.Sprintf("%s", t.id),
278+
doBenchmarkEntriesCount(t.count, t.bitwidth, t.datasize, t.keysize))
265279
}
266280
}
267281

268-
func doBenchmarkEntriesCount(num int, bitWidth int) func(b *testing.B) {
282+
func doBenchmarkEntriesCount(num int, bitWidth int, datasize int, keysize int) func(b *testing.B) {
269283
r := rander{rand.New(rand.NewSource(int64(num)))}
270284
return func(b *testing.B) {
271285
blockstore := newMockBlocks()
@@ -274,8 +288,8 @@ func doBenchmarkEntriesCount(num int, bitWidth int) func(b *testing.B) {
274288

275289
var keys []string
276290
for i := 0; i < num; i++ {
277-
k := r.randString()
278-
if err := n.Set(context.TODO(), k, r.randValue()); err != nil {
291+
k := r.randString(keysize)
292+
if err := n.Set(context.TODO(), k, r.randValue(datasize)); err != nil {
279293
b.Fatal(err)
280294
}
281295
keys = append(keys, k)

hamt_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func (mb *mockBlocks) Get(c cid.Cid) (block.Block, error) {
3838

3939
func (mb *mockBlocks) Put(b block.Block) error {
4040
mb.stats.evtcntPut++
41+
mb.stats.bytesPut += len(b.RawData())
4142
if _, exists := mb.data[b.Cid()]; exists {
4243
mb.stats.evtcntPutDup++
4344
}
@@ -48,6 +49,7 @@ func (mb *mockBlocks) Put(b block.Block) error {
4849
type blockstoreStats struct {
4950
evtcntGet int
5051
evtcntPut int
52+
bytesPut int
5153
evtcntPutDup int
5254
}
5355

0 commit comments

Comments
 (0)