From 22c494d3996db9d7a09c8e5fcbfd15592b36f57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 3 Dec 2019 10:00:26 +0200 Subject: [PATCH] core/state/snapshot: bloom, metrics and prefetcher fixes --- core/state/snapshot/difflayer.go | 36 ++++++++++++++++++++++---------- core/state/snapshot/disklayer.go | 14 +++++++++---- core/state/snapshot/snapshot.go | 7 +++++++ core/state_prefetcher.go | 11 ++++++++-- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 634118a10..05d55a6fa 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -43,9 +43,11 @@ var ( // aggregatorItemLimit is an approximate number of items that will end up // in the agregator layer before it's flushed out to disk. A plain account - // weighs around 14B (+hash), a storage slot 32B (+hash), so 50 is a very - // rough average of what we might see. - aggregatorItemLimit = aggregatorMemoryLimit / 55 + // weighs around 14B (+hash), a storage slot 32B (+hash), a deleted slot + // 0B (+hash). Slots are mostly set/unset in lockstep, so thet average at + // 16B (+hash). All in all, the average entry seems to be 15+32=47B. Use a + // smaller number to be on the safe side. + aggregatorItemLimit = aggregatorMemoryLimit / 42 // bloomTargetError is the target false positive rate when the aggregator // layer is at its fullest. The actual value will probably move around up @@ -269,13 +271,13 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) { return dl.origin.AccountRLP(hash) } // The bloom filter hit, start poking in the internal maps - return dl.accountRLP(hash) + return dl.accountRLP(hash, 0) } // accountRLP is an internal version of AccountRLP that skips the bloom filter // checks and uses the internal maps to try and retrieve the data. It's meant // to be used if a higher layer's bloom filter hit already. -func (dl *diffLayer) accountRLP(hash common.Hash) ([]byte, error) { +func (dl *diffLayer) accountRLP(hash common.Hash, depth int) ([]byte, error) { dl.lock.RLock() defer dl.lock.RUnlock() @@ -288,13 +290,18 @@ func (dl *diffLayer) accountRLP(hash common.Hash) ([]byte, error) { // deleted, and is a different notion than an unknown account! if data, ok := dl.accountData[hash]; ok { snapshotDirtyAccountHitMeter.Mark(1) - snapshotDirtyAccountReadMeter.Mark(int64(len(data))) + snapshotDirtyAccountHitDepthHist.Update(int64(depth)) + if n := len(data); n > 0 { + snapshotDirtyAccountReadMeter.Mark(int64(n)) + } else { + snapshotDirtyAccountInexMeter.Mark(1) + } snapshotBloomAccountTrueHitMeter.Mark(1) return data, nil } // Account unknown to this diff, resolve from parent if diff, ok := dl.parent.(*diffLayer); ok { - return diff.accountRLP(hash) + return diff.accountRLP(hash, depth+1) } // Failed to resolve through diff layers, mark a bloom error and use the disk snapshotBloomAccountFalseHitMeter.Mark(1) @@ -318,13 +325,13 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro return dl.origin.Storage(accountHash, storageHash) } // The bloom filter hit, start poking in the internal maps - return dl.storage(accountHash, storageHash) + return dl.storage(accountHash, storageHash, 0) } // storage is an internal version of Storage that skips the bloom filter checks // and uses the internal maps to try and retrieve the data. It's meant to be // used if a higher layer's bloom filter hit already. -func (dl *diffLayer) storage(accountHash, storageHash common.Hash) ([]byte, error) { +func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) { dl.lock.RLock() defer dl.lock.RUnlock() @@ -338,19 +345,26 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash) ([]byte, erro if storage, ok := dl.storageData[accountHash]; ok { if storage == nil { snapshotDirtyStorageHitMeter.Mark(1) + snapshotDirtyStorageHitDepthHist.Update(int64(depth)) + snapshotDirtyStorageInexMeter.Mark(1) snapshotBloomStorageTrueHitMeter.Mark(1) return nil, nil } if data, ok := storage[storageHash]; ok { snapshotDirtyStorageHitMeter.Mark(1) - snapshotDirtyStorageReadMeter.Mark(int64(len(data))) + snapshotDirtyStorageHitDepthHist.Update(int64(depth)) + if n := len(data); n > 0 { + snapshotDirtyStorageReadMeter.Mark(int64(n)) + } else { + snapshotDirtyStorageInexMeter.Mark(1) + } snapshotBloomStorageTrueHitMeter.Mark(1) return data, nil } } // Storage slot unknown to this diff, resolve from parent if diff, ok := dl.parent.(*diffLayer); ok { - return diff.storage(accountHash, storageHash) + return diff.storage(accountHash, storageHash, depth+1) } // Failed to resolve through diff layers, mark a bloom error and use the disk snapshotBloomStorageFalseHitMeter.Mark(1) diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index b1934d273..7c5b3e3e9 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -104,8 +104,11 @@ func (dl *diskLayer) AccountRLP(hash common.Hash) ([]byte, error) { dl.cache.Set(hash[:], blob) snapshotCleanAccountMissMeter.Mark(1) - snapshotCleanAccountWriteMeter.Mark(int64(len(blob))) - + if n := len(blob); n > 0 { + snapshotCleanAccountWriteMeter.Mark(int64(n)) + } else { + snapshotCleanAccountInexMeter.Mark(1) + } return blob, nil } @@ -141,8 +144,11 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro dl.cache.Set(key, blob) snapshotCleanStorageMissMeter.Mark(1) - snapshotCleanStorageWriteMeter.Mark(int64(len(blob))) - + if n := len(blob); n > 0 { + snapshotCleanStorageWriteMeter.Mark(int64(n)) + } else { + snapshotCleanStorageInexMeter.Mark(1) + } return blob, nil } diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 749f61078..7650cf2c1 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -34,24 +34,31 @@ import ( var ( snapshotCleanAccountHitMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/hit", nil) snapshotCleanAccountMissMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/miss", nil) + snapshotCleanAccountInexMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/inex", nil) snapshotCleanAccountReadMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/read", nil) snapshotCleanAccountWriteMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/write", nil) snapshotCleanStorageHitMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/hit", nil) snapshotCleanStorageMissMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/miss", nil) + snapshotCleanStorageInexMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/inex", nil) snapshotCleanStorageReadMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/read", nil) snapshotCleanStorageWriteMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/write", nil) snapshotDirtyAccountHitMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/hit", nil) snapshotDirtyAccountMissMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/miss", nil) + snapshotDirtyAccountInexMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/inex", nil) snapshotDirtyAccountReadMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/read", nil) snapshotDirtyAccountWriteMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/write", nil) snapshotDirtyStorageHitMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/hit", nil) snapshotDirtyStorageMissMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/miss", nil) + snapshotDirtyStorageInexMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/inex", nil) snapshotDirtyStorageReadMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/read", nil) snapshotDirtyStorageWriteMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/write", nil) + snapshotDirtyAccountHitDepthHist = metrics.NewRegisteredHistogram("state/snapshot/dirty/account/hit/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) + snapshotDirtyStorageHitDepthHist = metrics.NewRegisteredHistogram("state/snapshot/dirty/storage/hit/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) + snapshotFlushAccountItemMeter = metrics.NewRegisteredMeter("state/snapshot/flush/account/item", nil) snapshotFlushAccountSizeMeter = metrics.NewRegisteredMeter("state/snapshot/flush/account/size", nil) snapshotFlushStorageItemMeter = metrics.NewRegisteredMeter("state/snapshot/flush/storage/item", nil) diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index bb5db4ced..2624f38db 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -54,6 +54,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c gaspool = new(GasPool).AddGas(block.GasLimit()) ) // Iterate over and process the individual transactions + byzantium := p.config.IsByzantium(block.Number()) for i, tx := range block.Transactions() { // If block precaching was interrupted, abort if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { @@ -64,9 +65,15 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg); err != nil { return // Ugh, something went horribly wrong, bail out } + // If we're pre-byzantium, pre-load trie nodes for the intermediate root + if !byzantium { + statedb.IntermediateRoot(true) + } + } + // If were post-byzantium, pre-load trie nodes for the final root hash + if byzantium { + statedb.IntermediateRoot(true) } - // All transactions processed, finalize the block to force loading written-only trie paths - statedb.Finalise(true) // TODO(karalabe): should we run this on interrupt too? } // precacheTransaction attempts to apply a transaction to the given state database