From 8d7e1e9374252b97a281cb4d198f36bfda861728 Mon Sep 17 00:00:00 2001 From: Alex Sharov Date: Wed, 25 Mar 2020 16:43:55 +0700 Subject: [PATCH] Optimize resolver cached (#404) * remove allocations related to "remove incarnation" actions * invalidate startKeyNoInc when startKey changed * dbutils.RemoveIncarnationFromKey - doesn't do allocation --- common/dbutils/composite_keys.go | 15 ++++++++------- core/state/database.go | 15 ++++++++++++--- trie/resolver_stateful_cached.go | 26 +++++++++++++++++++++----- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/common/dbutils/composite_keys.go b/common/dbutils/composite_keys.go index e68b7149b..fa06e765f 100644 --- a/common/dbutils/composite_keys.go +++ b/common/dbutils/composite_keys.go @@ -119,14 +119,15 @@ func DecodeIncarnation(buf []byte) uint64 { return incarnation ^ ^uint64(0) } -func RemoveIncarnationFromKey(storageKey []byte) []byte { - if len(storageKey) <= common.HashLength { - return storageKey +func RemoveIncarnationFromKey(key []byte, buf *[]byte) { + tmp := *buf + if len(key) <= common.HashLength { + tmp = append(tmp, key...) + } else { + tmp = append(tmp, key[:common.HashLength]...) + tmp = append(tmp, key[common.HashLength+8:]...) } - buf := make([]byte, 0, common.HashLength*2) - buf = append(buf, storageKey[:common.HashLength]...) - buf = append(buf, storageKey[common.HashLength+8:]...) - return buf + *buf = tmp } // Key + blockNum diff --git a/core/state/database.go b/core/state/database.go index 14ccbf98f..a7e7ab604 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -294,6 +294,9 @@ func ClearTombstonesForReCreatedAccount(db ethdb.MinDatabase, addrHash common.Ha return nil } + buf := pool.GetBuffer(256) + defer pool.PutBuffer(buf) + incarnation := dbutils.DecodeIncarnation(k[common.HashLength : common.HashLength+8]) for ; incarnation > 0; incarnation-- { accWithInc := dbutils.GenerateStoragePrefix(addrHash, incarnation) @@ -306,8 +309,9 @@ func ClearTombstonesForReCreatedAccount(db ethdb.MinDatabase, addrHash common.Ha break } - kNoInc := dbutils.RemoveIncarnationFromKey(k) - toPut = append(toPut, common.CopyBytes(kNoInc[:common.HashLength+1])) + buf.Reset() + dbutils.RemoveIncarnationFromKey(k, &buf.B) + toPut = append(toPut, common.CopyBytes(buf.B[:common.HashLength+1])) } } return nil @@ -412,6 +416,9 @@ func ClearTombstonesForNewStorage(db ethdb.MinDatabase, storageKeyNoInc []byte) kWithInc.B = append(kWithInc.B, storageK[:common.HashLength+8]...) kWithInc.B = append(kWithInc.B, storageKeyNoInc[common.HashLength:]...) + buf := pool.GetBuffer(256) + defer pool.PutBuffer(buf) + for i := common.HashLength + 1; i < len(storageKeyNoInc)-1; i++ { // +1 because first step happened during account re-creation tombStone, _ := interBucket.Get(storageKeyNoInc[:i]) foundTombstone := tombStone != nil && len(tombStone) == 0 @@ -431,7 +438,9 @@ func ClearTombstonesForNewStorage(db ethdb.MinDatabase, storageKeyNoInc []byte) continue } - toPut = append(toPut, dbutils.RemoveIncarnationFromKey(storageK[:i+1+8])) + buf.Reset() + dbutils.RemoveIncarnationFromKey(storageK[:i+1+8], &buf.B) + toPut = append(toPut, common.CopyBytes(buf.B)) } toDelete = append(toDelete, common.CopyBytes(storageKeyNoInc[:i])) break diff --git a/trie/resolver_stateful_cached.go b/trie/resolver_stateful_cached.go index a45051326..8ea8778c9 100644 --- a/trie/resolver_stateful_cached.go +++ b/trie/resolver_stateful_cached.go @@ -343,9 +343,16 @@ func (tr *ResolverStatefulCached) MultiWalk2(db *bolt.DB, blockNr uint64, bucket keyAsNibbles := pool.GetBuffer(256) defer pool.PutBuffer(keyAsNibbles) + startKeyNoInc := pool.GetBuffer(common.HashLength * 2) + defer pool.PutBuffer(startKeyNoInc) + rangeIdx := 0 // What is the current range we are extracting fixedbytes, mask := ethdb.Bytesmask(fixedbits[rangeIdx]) startkey := startkeys[rangeIdx] + + startKeyNoInc.Reset() + dbutils.RemoveIncarnationFromKey(startkey, &startKeyNoInc.B) + err := db.View(func(tx *bolt.Tx) error { cacheBucket := tx.Bucket(dbutils.IntermediateTrieHashBucket) if cacheBucket == nil { @@ -360,7 +367,7 @@ func (tr *ResolverStatefulCached) MultiWalk2(db *bolt.DB, blockNr uint64, bucket c := b.Cursor() k, v := c.Seek(startkey) - cacheK, cacheV := cache.Seek(dbutils.RemoveIncarnationFromKey(startkey)) + cacheK, cacheV := cache.Seek(startKeyNoInc.B) var minKey []byte var fromCache bool @@ -391,7 +398,7 @@ func (tr *ResolverStatefulCached) MultiWalk2(db *bolt.DB, blockNr uint64, bucket startKeyIndex = minInt(len(startkey), fixedbytes-1-8) cmp = bytes.Compare(minKey[:minKeyIndex], startkey[:startKeyIndex]) } else if fromCache && fixedbytes > 40 { - cmp = bytes.Compare(minKey[:minKeyIndex], dbutils.RemoveIncarnationFromKey(startkey[:startKeyIndex])) + cmp = bytes.Compare(minKey[:minKeyIndex], startKeyNoInc.B[:startKeyIndex]) } else { cmp = bytes.Compare(minKey[:minKeyIndex], startkey[:startKeyIndex]) } @@ -411,7 +418,7 @@ func (tr *ResolverStatefulCached) MultiWalk2(db *bolt.DB, blockNr uint64, bucket } if cmp < 0 { k, v = c.SeekTo(startkey) - cacheK, cacheV = cache.SeekTo(dbutils.RemoveIncarnationFromKey(startkey)) + cacheK, cacheV = cache.SeekTo(startKeyNoInc.B) // for Address bucket, skip cache keys longer than 31 bytes if isAccountBucket && len(cacheK) > maxAccountKeyLen { for len(cacheK) > maxAccountKeyLen { @@ -430,6 +437,8 @@ func (tr *ResolverStatefulCached) MultiWalk2(db *bolt.DB, blockNr uint64, bucket } fixedbytes, mask = ethdb.Bytesmask(fixedbits[rangeIdx]) startkey = startkeys[rangeIdx] + startKeyNoInc.Reset() + dbutils.RemoveIncarnationFromKey(startkey, &startKeyNoInc.B) } } } @@ -495,11 +504,18 @@ func (tr *ResolverStatefulCached) MultiWalk2(db *bolt.DB, blockNr uint64, bucket continue } - if isAccountBucket { + if isAccountBucket || len(cacheK) <= common.HashLength { k, v = c.Seek(next) } else { // skip subtree - can't .Seek because storage bucket has incarnation in keys - for k, v = c.Next(); bytes.HasPrefix(dbutils.RemoveIncarnationFromKey(k), cacheK); k, v = c.Next() { + for k, v = c.Next(); k != nil; k, v = c.Next() { + matchAcc := bytes.HasPrefix(k[:common.HashLength], next[:common.HashLength]) + // don't check incarnation ... + matchStorage := bytes.HasPrefix(k[common.HashLength+8:], next[common.HashLength:]) + + if matchAcc && matchStorage { + break + } } }