Skip to content

Commit 911a426

Browse files
committed
feat(blockstm): cache pre-state to optimize value-based validation
skip cache oversized
1 parent aed7a9e commit 911a426

File tree

3 files changed

+50
-6
lines changed

3 files changed

+50
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
9999
* (blockstm) [25883](https://github.com/cosmos/cosmos-sdk/pull/25883) Re-use decoded tx object in pre-estimates.
100100
* (blockstm) [#25788](https://github.com/cosmos/cosmos-sdk/pull/25788) Only validate transactions that's executed at lease once.
101101
* (blockstm) [#25767](https://github.com/cosmos/cosmos-sdk/pull/25767) Optimize block-stm MVMemory with bitmap index.
102+
* (blockstm) [#25909](https://github.com/cosmos/cosmos-sdk/pull/25909) Cache pre-state to optimize value-based validation.
102103

103104
### Bug Fixes
104105

internal/blockstm/mvmemory.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package blockstm
22

33
import (
4+
"sync"
45
"sync/atomic"
56

67
storetypes "cosmossdk.io/store/types"
@@ -17,9 +18,26 @@ type MVMemory struct {
1718
scheduler *Scheduler
1819
stores map[storetypes.StoreKey]int
1920
data []MVStore
21+
preState []preStateCache
2022
lastReadSet []atomic.Pointer[MultiReadSet]
2123
}
2224

25+
type preStateCache struct {
26+
data sync.Map
27+
}
28+
29+
func (c *preStateCache) Load(key Key) (any, bool) {
30+
v, ok := c.data.Load(string(key))
31+
if !ok {
32+
return nil, false
33+
}
34+
return v, true
35+
}
36+
37+
func (c *preStateCache) Store(key Key, value any) {
38+
c.data.Store(string(key), value)
39+
}
40+
2341
func NewMVMemory(
2442
block_size int, stores map[storetypes.StoreKey]int,
2543
storage MultiStore, scheduler *Scheduler,
@@ -41,6 +59,7 @@ func NewMVMemoryWithEstimates(
4159
scheduler: scheduler,
4260
stores: stores,
4361
data: data,
62+
preState: make([]preStateCache, len(stores)),
4463
lastReadSet: make([]atomic.Pointer[MultiReadSet], block_size),
4564
}
4665

@@ -97,7 +116,7 @@ func (mv *MVMemory) View(txn TxnIndex) *MultiMVMemoryView {
97116

98117
func (mv *MVMemory) newMVView(name storetypes.StoreKey, txn TxnIndex) MVView {
99118
i := mv.stores[name]
100-
return NewMVView(i, mv.storage.GetStore(name), mv.GetMVStore(i), mv.scheduler, txn)
119+
return NewMVView(i, mv.storage.GetStore(name), mv.GetMVStore(i), mv.scheduler, txn, &mv.preState[i])
101120
}
102121

103122
func (mv *MVMemory) GetMVStore(i int) MVStore {

internal/blockstm/mvview.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/cosmos/cosmos-sdk/telemetry"
1212
)
1313

14+
const preStateMaxCacheValueBytes = 4096
15+
1416
var (
1517
_ storetypes.KVStore = (*GMVMemoryView[[]byte])(nil)
1618
_ storetypes.ObjKVStore = (*GMVMemoryView[any])(nil)
@@ -23,35 +25,57 @@ type GMVMemoryView[V any] struct {
2325
storage storetypes.GKVStore[V]
2426
mvData *GMVData[V]
2527
scheduler *Scheduler
28+
preState *preStateCache
2629
store int
2730

2831
txn TxnIndex
2932
readSet *ReadSet
3033
writeSet *GMemDB[V]
3134
}
3235

33-
func NewMVView(store int, storage storetypes.Store, mvData MVStore, scheduler *Scheduler, txn TxnIndex) MVView {
36+
func NewMVView(store int, storage storetypes.Store, mvData MVStore, scheduler *Scheduler, txn TxnIndex, preState *preStateCache) MVView {
3437
switch data := mvData.(type) {
3538
case *GMVData[any]:
36-
return NewGMVMemoryView(store, storage.(storetypes.ObjKVStore), data, scheduler, txn)
39+
return NewGMVMemoryView(store, storage.(storetypes.ObjKVStore), data, scheduler, txn, preState)
3740
case *GMVData[[]byte]:
38-
return NewGMVMemoryView(store, storage.(storetypes.KVStore), data, scheduler, txn)
41+
return NewGMVMemoryView(store, storage.(storetypes.KVStore), data, scheduler, txn, preState)
3942
default:
4043
panic("unsupported value type")
4144
}
4245
}
4346

44-
func NewGMVMemoryView[V any](store int, storage storetypes.GKVStore[V], mvData *GMVData[V], scheduler *Scheduler, txn TxnIndex) *GMVMemoryView[V] {
47+
func NewGMVMemoryView[V any](store int, storage storetypes.GKVStore[V], mvData *GMVData[V], scheduler *Scheduler, txn TxnIndex, preState *preStateCache) *GMVMemoryView[V] {
4548
return &GMVMemoryView[V]{
4649
store: store,
4750
storage: storage,
4851
mvData: mvData,
4952
scheduler: scheduler,
53+
preState: preState,
5054
txn: txn,
5155
readSet: new(ReadSet),
5256
}
5357
}
5458

59+
func (s *GMVMemoryView[V]) getFromPreState(key []byte) V {
60+
if s.preState != nil {
61+
if cached, ok := s.preState.Load(key); ok {
62+
return cached.(V)
63+
}
64+
}
65+
66+
result := s.storage.Get(key)
67+
if s.preState != nil {
68+
if bytesValue, ok := any(result).([]byte); ok {
69+
if len(bytesValue) <= preStateMaxCacheValueBytes {
70+
s.preState.Store(key, any(result))
71+
}
72+
} else {
73+
s.preState.Store(key, any(result))
74+
}
75+
}
76+
return result
77+
}
78+
5579
func (s *GMVMemoryView[V]) init() {
5680
if s.writeSet == nil {
5781
s.writeSet = NewWriteSet(s.mvData.isZero, s.mvData.valueLen)
@@ -105,7 +129,7 @@ func (s *GMVMemoryView[V]) Get(key []byte) V {
105129
// if not found, record version ⊥ when reading from storage.
106130
s.readSet.Reads = append(s.readSet.Reads, ReadDescriptor{key, version})
107131
if !version.Valid() {
108-
result := s.storage.Get(key)
132+
result := s.getFromPreState(key)
109133
telemetry.MeasureSince(start, TelemetrySubsystem, KeyMVViewReadStorage) //nolint:staticcheck // TODO: switch to OpenTelemetry
110134
return result
111135
}

0 commit comments

Comments
 (0)