From a487729d83ece12482eb1e7caf4ab1889ca68315 Mon Sep 17 00:00:00 2001 From: Delweng Date: Thu, 17 Jul 2025 11:07:22 +0800 Subject: [PATCH] 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 --- triedb/pathdb/history_index_block.go | 33 +++++++++++------------ triedb/pathdb/history_index_block_test.go | 18 +++++++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/triedb/pathdb/history_index_block.go b/triedb/pathdb/history_index_block.go index 7b85667e9d..10cc88ed4e 100644 --- a/triedb/pathdb/history_index_block.go +++ b/triedb/pathdb/history_index_block.go @@ -116,34 +116,31 @@ func parseIndexBlock(blob []byte) ([]uint16, []byte, error) { if len(blob) < 1 { return nil, nil, fmt.Errorf("corrupted index block, len: %d", len(blob)) } - restartLen := blob[len(blob)-1] + restartLen := int(blob[len(blob)-1]) if restartLen == 0 { return nil, nil, errors.New("corrupted index block, no restart") } - tailLen := int(restartLen)*2 + 1 + tailLen := restartLen*2 + 1 if len(blob) < tailLen { return nil, nil, fmt.Errorf("truncated restarts, size: %d, restarts: %d", len(blob), restartLen) } - restarts := make([]uint16, 0, restartLen) - for i := int(restartLen); i > 0; i-- { - restart := binary.BigEndian.Uint16(blob[len(blob)-1-2*i:]) - restarts = append(restarts, restart) - } - // Validate that restart points are strictly ordered and within the valid + restarts := make([]uint16, restartLen) + dataEnd := len(blob) - tailLen + + // Extract and validate that restart points are strictly ordered and within the valid // data range. - var prev uint16 - for i := 0; i < len(restarts); i++ { - if i != 0 { - if restarts[i] <= prev { - return nil, nil, fmt.Errorf("restart out of order, prev: %d, next: %d", prev, restarts[i]) - } + for i := 0; i < restartLen; i++ { + off := dataEnd + 2*i + restarts[i] = binary.BigEndian.Uint16(blob[off : off+2]) + + if i > 0 && restarts[i] <= restarts[i-1] { + return nil, nil, fmt.Errorf("restart out of order, prev: %d, next: %d", restarts[i-1], restarts[i]) } - if int(restarts[i]) >= len(blob)-tailLen { - return nil, nil, fmt.Errorf("invalid restart position, restart: %d, size: %d", restarts[i], len(blob)-tailLen) + if int(restarts[i]) >= dataEnd { + return nil, nil, fmt.Errorf("invalid restart position, restart: %d, size: %d", restarts[i], dataEnd) } - prev = restarts[i] } - return restarts, blob[:len(blob)-tailLen], nil + return restarts, blob[:dataEnd], nil } // blockReader is the reader to access the element within a block. diff --git a/triedb/pathdb/history_index_block_test.go b/triedb/pathdb/history_index_block_test.go index 173387b447..7b0e362c66 100644 --- a/triedb/pathdb/history_index_block_test.go +++ b/triedb/pathdb/history_index_block_test.go @@ -214,3 +214,21 @@ func TestCorruptedIndexBlock(t *testing.T) { t.Fatal("Corrupted index block data is not detected") } } + +// BenchmarkParseIndexBlock benchmarks the performance of parseIndexBlock. +func BenchmarkParseIndexBlock(b *testing.B) { + // Generate a realistic index block blob + bw, _ := newBlockWriter(nil, newIndexBlockDesc(0)) + for i := 0; i < 4096; i++ { + bw.append(uint64(i * 2)) + } + blob := bw.finish() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := parseIndexBlock(blob) + if err != nil { + b.Fatalf("parseIndexBlock failed: %v", err) + } + } +}