Skip to content

Commit a487729

Browse files
authored
triedb/pathdb: improve the performance of parse index block (#32219)
The implementation of `parseIndexBlock` used a reverse loop with slice appends to build the restart points, which was less cache-friendly and involved unnecessary allocations and operations. In this PR we change the implementation to read and validate the restart points in one single forward loop. Here is the benchmark test: ```bash go test -benchmem -bench=BenchmarkParseIndexBlock ./triedb/pathdb/ ``` The result as below: ``` benchmark old ns/op new ns/op delta BenchmarkParseIndexBlock-8 52.9 37.5 -29.05% ``` about 29% improvements --------- Signed-off-by: jsvisa <delweng@gmail.com>
1 parent becca46 commit a487729

File tree

2 files changed

+33
-18
lines changed

2 files changed

+33
-18
lines changed

triedb/pathdb/history_index_block.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,34 +116,31 @@ func parseIndexBlock(blob []byte) ([]uint16, []byte, error) {
116116
if len(blob) < 1 {
117117
return nil, nil, fmt.Errorf("corrupted index block, len: %d", len(blob))
118118
}
119-
restartLen := blob[len(blob)-1]
119+
restartLen := int(blob[len(blob)-1])
120120
if restartLen == 0 {
121121
return nil, nil, errors.New("corrupted index block, no restart")
122122
}
123-
tailLen := int(restartLen)*2 + 1
123+
tailLen := restartLen*2 + 1
124124
if len(blob) < tailLen {
125125
return nil, nil, fmt.Errorf("truncated restarts, size: %d, restarts: %d", len(blob), restartLen)
126126
}
127-
restarts := make([]uint16, 0, restartLen)
128-
for i := int(restartLen); i > 0; i-- {
129-
restart := binary.BigEndian.Uint16(blob[len(blob)-1-2*i:])
130-
restarts = append(restarts, restart)
131-
}
132-
// Validate that restart points are strictly ordered and within the valid
127+
restarts := make([]uint16, restartLen)
128+
dataEnd := len(blob) - tailLen
129+
130+
// Extract and validate that restart points are strictly ordered and within the valid
133131
// data range.
134-
var prev uint16
135-
for i := 0; i < len(restarts); i++ {
136-
if i != 0 {
137-
if restarts[i] <= prev {
138-
return nil, nil, fmt.Errorf("restart out of order, prev: %d, next: %d", prev, restarts[i])
139-
}
132+
for i := 0; i < restartLen; i++ {
133+
off := dataEnd + 2*i
134+
restarts[i] = binary.BigEndian.Uint16(blob[off : off+2])
135+
136+
if i > 0 && restarts[i] <= restarts[i-1] {
137+
return nil, nil, fmt.Errorf("restart out of order, prev: %d, next: %d", restarts[i-1], restarts[i])
140138
}
141-
if int(restarts[i]) >= len(blob)-tailLen {
142-
return nil, nil, fmt.Errorf("invalid restart position, restart: %d, size: %d", restarts[i], len(blob)-tailLen)
139+
if int(restarts[i]) >= dataEnd {
140+
return nil, nil, fmt.Errorf("invalid restart position, restart: %d, size: %d", restarts[i], dataEnd)
143141
}
144-
prev = restarts[i]
145142
}
146-
return restarts, blob[:len(blob)-tailLen], nil
143+
return restarts, blob[:dataEnd], nil
147144
}
148145

149146
// blockReader is the reader to access the element within a block.

triedb/pathdb/history_index_block_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,21 @@ func TestCorruptedIndexBlock(t *testing.T) {
214214
t.Fatal("Corrupted index block data is not detected")
215215
}
216216
}
217+
218+
// BenchmarkParseIndexBlock benchmarks the performance of parseIndexBlock.
219+
func BenchmarkParseIndexBlock(b *testing.B) {
220+
// Generate a realistic index block blob
221+
bw, _ := newBlockWriter(nil, newIndexBlockDesc(0))
222+
for i := 0; i < 4096; i++ {
223+
bw.append(uint64(i * 2))
224+
}
225+
blob := bw.finish()
226+
227+
b.ResetTimer()
228+
for i := 0; i < b.N; i++ {
229+
_, _, err := parseIndexBlock(blob)
230+
if err != nil {
231+
b.Fatalf("parseIndexBlock failed: %v", err)
232+
}
233+
}
234+
}

0 commit comments

Comments
 (0)