mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 15:47:21 +00:00
core/state: add cache statistics of contract code reader (#33532)
This commit is contained in:
parent
01b39c96bf
commit
9623dcbca2
2 changed files with 77 additions and 36 deletions
|
|
@ -177,8 +177,8 @@ func NewDatabaseForTesting() *CachingDB {
|
||||||
return NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
return NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reader returns a state reader associated with the specified state root.
|
// StateReader returns a state reader associated with the specified state root.
|
||||||
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
func (db *CachingDB) StateReader(stateRoot common.Hash) (StateReader, error) {
|
||||||
var readers []StateReader
|
var readers []StateReader
|
||||||
|
|
||||||
// Configure the state reader using the standalone snapshot in hash mode.
|
// Configure the state reader using the standalone snapshot in hash mode.
|
||||||
|
|
@ -208,23 +208,32 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
}
|
}
|
||||||
readers = append(readers, tr)
|
readers = append(readers, tr)
|
||||||
|
|
||||||
combined, err := newMultiStateReader(readers...)
|
return newMultiStateReader(readers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader implements Database, returning a reader associated with the specified
|
||||||
|
// state root.
|
||||||
|
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
|
sr, err := db.StateReader(stateRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), sr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadersWithCacheStats creates a pair of state readers sharing the same internal cache and
|
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||||
// same backing Reader, but exposing separate statistics.
|
// underlying state reader and internal state cache, while maintaining separate
|
||||||
// and statistics.
|
// statistics respectively.
|
||||||
func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (ReaderWithStats, ReaderWithStats, error) {
|
func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (ReaderWithStats, ReaderWithStats, error) {
|
||||||
reader, err := db.Reader(stateRoot)
|
r, err := db.StateReader(stateRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
shared := newReaderWithCache(reader)
|
sr := newStateReaderWithCache(r)
|
||||||
return newReaderWithCacheStats(shared), newReaderWithCacheStats(shared), nil
|
|
||||||
|
ra := newReaderWithStats(sr, newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache))
|
||||||
|
rb := newReaderWithStats(sr, newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache))
|
||||||
|
return ra, rb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenTrie opens the main account trie at a specific root hash.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,13 @@ type ContractCodeReader interface {
|
||||||
CodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
CodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContractCodeReaderWithStats extends ContractCodeReader by adding GetStats to
|
||||||
|
// expose statistics of code reader.
|
||||||
|
type ContractCodeReaderWithStats interface {
|
||||||
|
ContractCodeReader
|
||||||
|
GetStats() (int64, int64)
|
||||||
|
}
|
||||||
|
|
||||||
// StateReader defines the interface for accessing accounts and storage slots
|
// StateReader defines the interface for accessing accounts and storage slots
|
||||||
// associated with a specific state.
|
// associated with a specific state.
|
||||||
//
|
//
|
||||||
|
|
@ -97,6 +104,8 @@ type ReaderStats struct {
|
||||||
AccountCacheMiss int64
|
AccountCacheMiss int64
|
||||||
StorageCacheHit int64
|
StorageCacheHit int64
|
||||||
StorageCacheMiss int64
|
StorageCacheMiss int64
|
||||||
|
ContractCodeHit int64
|
||||||
|
ContractCodeMiss int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements fmt.Stringer, returning string format statistics.
|
// String implements fmt.Stringer, returning string format statistics.
|
||||||
|
|
@ -104,6 +113,7 @@ func (s ReaderStats) String() string {
|
||||||
var (
|
var (
|
||||||
accountCacheHitRate float64
|
accountCacheHitRate float64
|
||||||
storageCacheHitRate float64
|
storageCacheHitRate float64
|
||||||
|
contractCodeHitRate float64
|
||||||
)
|
)
|
||||||
if s.AccountCacheHit > 0 {
|
if s.AccountCacheHit > 0 {
|
||||||
accountCacheHitRate = float64(s.AccountCacheHit) / float64(s.AccountCacheHit+s.AccountCacheMiss) * 100
|
accountCacheHitRate = float64(s.AccountCacheHit) / float64(s.AccountCacheHit+s.AccountCacheMiss) * 100
|
||||||
|
|
@ -111,9 +121,13 @@ func (s ReaderStats) String() string {
|
||||||
if s.StorageCacheHit > 0 {
|
if s.StorageCacheHit > 0 {
|
||||||
storageCacheHitRate = float64(s.StorageCacheHit) / float64(s.StorageCacheHit+s.StorageCacheMiss) * 100
|
storageCacheHitRate = float64(s.StorageCacheHit) / float64(s.StorageCacheHit+s.StorageCacheMiss) * 100
|
||||||
}
|
}
|
||||||
|
if s.ContractCodeHit > 0 {
|
||||||
|
contractCodeHitRate = float64(s.ContractCodeHit) / float64(s.ContractCodeHit+s.ContractCodeMiss) * 100
|
||||||
|
}
|
||||||
msg := fmt.Sprintf("Reader statistics\n")
|
msg := fmt.Sprintf("Reader statistics\n")
|
||||||
msg += fmt.Sprintf("account: hit: %d, miss: %d, rate: %.2f\n", s.AccountCacheHit, s.AccountCacheMiss, accountCacheHitRate)
|
msg += fmt.Sprintf("account: hit: %d, miss: %d, rate: %.2f\n", s.AccountCacheHit, s.AccountCacheMiss, accountCacheHitRate)
|
||||||
msg += fmt.Sprintf("storage: hit: %d, miss: %d, rate: %.2f\n", s.StorageCacheHit, s.StorageCacheMiss, storageCacheHitRate)
|
msg += fmt.Sprintf("storage: hit: %d, miss: %d, rate: %.2f\n", s.StorageCacheHit, s.StorageCacheMiss, storageCacheHitRate)
|
||||||
|
msg += fmt.Sprintf("code: hit: %d, miss: %d, rate: %.2f\n", s.ContractCodeHit, s.ContractCodeMiss, contractCodeHitRate)
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +148,10 @@ type cachingCodeReader struct {
|
||||||
// they are natively thread-safe.
|
// they are natively thread-safe.
|
||||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||||
codeSizeCache *lru.Cache[common.Hash, int]
|
codeSizeCache *lru.Cache[common.Hash, int]
|
||||||
|
|
||||||
|
// Cache statistics
|
||||||
|
hit atomic.Int64 // Number of code lookups found in the cache.
|
||||||
|
miss atomic.Int64 // Number of code lookups not found in the cache.
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCachingCodeReader constructs the code reader.
|
// newCachingCodeReader constructs the code reader.
|
||||||
|
|
@ -150,8 +168,11 @@ func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstraine
|
||||||
func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
|
func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
|
||||||
code, _ := r.codeCache.Get(codeHash)
|
code, _ := r.codeCache.Get(codeHash)
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
|
r.hit.Add(1)
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
r.miss.Add(1)
|
||||||
|
|
||||||
code = rawdb.ReadCode(r.db, codeHash)
|
code = rawdb.ReadCode(r.db, codeHash)
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
r.codeCache.Add(codeHash, code)
|
r.codeCache.Add(codeHash, code)
|
||||||
|
|
@ -164,6 +185,7 @@ func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]b
|
||||||
// If the contract code doesn't exist, no error will be returned.
|
// If the contract code doesn't exist, no error will be returned.
|
||||||
func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||||
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
|
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
|
||||||
|
r.hit.Add(1)
|
||||||
return cached, nil
|
return cached, nil
|
||||||
}
|
}
|
||||||
code, err := r.Code(addr, codeHash)
|
code, err := r.Code(addr, codeHash)
|
||||||
|
|
@ -180,6 +202,11 @@ func (r *cachingCodeReader) Has(addr common.Address, codeHash common.Hash) bool
|
||||||
return len(code) > 0
|
return len(code) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStats returns the cache statistics fo the code reader.
|
||||||
|
func (r *cachingCodeReader) GetStats() (int64, int64) {
|
||||||
|
return r.hit.Load(), r.miss.Load()
|
||||||
|
}
|
||||||
|
|
||||||
// flatReader wraps a database state reader and is safe for concurrent access.
|
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||||
type flatReader struct {
|
type flatReader struct {
|
||||||
reader database.StateReader
|
reader database.StateReader
|
||||||
|
|
@ -462,10 +489,10 @@ func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readerWithCache is a wrapper around Reader that maintains additional state caches
|
// stateReaderWithCache is a wrapper around StateReader that maintains additional
|
||||||
// to support concurrent state access.
|
// state caches to support concurrent state access.
|
||||||
type readerWithCache struct {
|
type stateReaderWithCache struct {
|
||||||
Reader // safe for concurrent read
|
StateReader
|
||||||
|
|
||||||
// Previously resolved state entries.
|
// Previously resolved state entries.
|
||||||
accounts map[common.Address]*types.StateAccount
|
accounts map[common.Address]*types.StateAccount
|
||||||
|
|
@ -481,11 +508,11 @@ type readerWithCache struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newReaderWithCache constructs the reader with local cache.
|
// newStateReaderWithCache constructs the state reader with local cache.
|
||||||
func newReaderWithCache(reader Reader) *readerWithCache {
|
func newStateReaderWithCache(sr StateReader) *stateReaderWithCache {
|
||||||
r := &readerWithCache{
|
r := &stateReaderWithCache{
|
||||||
Reader: reader,
|
StateReader: sr,
|
||||||
accounts: make(map[common.Address]*types.StateAccount),
|
accounts: make(map[common.Address]*types.StateAccount),
|
||||||
}
|
}
|
||||||
for i := range r.storageBuckets {
|
for i := range r.storageBuckets {
|
||||||
r.storageBuckets[i].storages = make(map[common.Address]map[common.Hash]common.Hash)
|
r.storageBuckets[i].storages = make(map[common.Address]map[common.Hash]common.Hash)
|
||||||
|
|
@ -498,7 +525,7 @@ func newReaderWithCache(reader Reader) *readerWithCache {
|
||||||
// might be nil if it's not existent.
|
// might be nil if it's not existent.
|
||||||
//
|
//
|
||||||
// An error will be returned if the state is corrupted in the underlying reader.
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
func (r *readerWithCache) account(addr common.Address) (*types.StateAccount, bool, error) {
|
func (r *stateReaderWithCache) account(addr common.Address) (*types.StateAccount, bool, error) {
|
||||||
// Try to resolve the requested account in the local cache
|
// Try to resolve the requested account in the local cache
|
||||||
r.accountLock.RLock()
|
r.accountLock.RLock()
|
||||||
acct, ok := r.accounts[addr]
|
acct, ok := r.accounts[addr]
|
||||||
|
|
@ -507,7 +534,7 @@ func (r *readerWithCache) account(addr common.Address) (*types.StateAccount, boo
|
||||||
return acct, true, nil
|
return acct, true, nil
|
||||||
}
|
}
|
||||||
// Try to resolve the requested account from the underlying reader
|
// Try to resolve the requested account from the underlying reader
|
||||||
acct, err := r.Reader.Account(addr)
|
acct, err := r.StateReader.Account(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -521,7 +548,7 @@ func (r *readerWithCache) account(addr common.Address) (*types.StateAccount, boo
|
||||||
// The returned account might be nil if it's not existent.
|
// The returned account might be nil if it's not existent.
|
||||||
//
|
//
|
||||||
// An error will be returned if the state is corrupted in the underlying reader.
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
func (r *readerWithCache) Account(addr common.Address) (*types.StateAccount, error) {
|
func (r *stateReaderWithCache) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
account, _, err := r.account(addr)
|
account, _, err := r.account(addr)
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
@ -529,7 +556,7 @@ func (r *readerWithCache) Account(addr common.Address) (*types.StateAccount, err
|
||||||
// storage retrieves the storage slot specified by the address and slot key, along
|
// storage retrieves the storage slot specified by the address and slot key, along
|
||||||
// with a flag indicating whether it's found in the cache or not. The returned
|
// with a flag indicating whether it's found in the cache or not. The returned
|
||||||
// storage slot might be empty if it's not existent.
|
// storage slot might be empty if it's not existent.
|
||||||
func (r *readerWithCache) storage(addr common.Address, slot common.Hash) (common.Hash, bool, error) {
|
func (r *stateReaderWithCache) storage(addr common.Address, slot common.Hash) (common.Hash, bool, error) {
|
||||||
var (
|
var (
|
||||||
value common.Hash
|
value common.Hash
|
||||||
ok bool
|
ok bool
|
||||||
|
|
@ -546,7 +573,7 @@ func (r *readerWithCache) storage(addr common.Address, slot common.Hash) (common
|
||||||
return value, true, nil
|
return value, true, nil
|
||||||
}
|
}
|
||||||
// Try to resolve the requested storage slot from the underlying reader
|
// Try to resolve the requested storage slot from the underlying reader
|
||||||
value, err := r.Reader.Storage(addr, slot)
|
value, err := r.StateReader.Storage(addr, slot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, false, err
|
return common.Hash{}, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -567,13 +594,14 @@ func (r *readerWithCache) storage(addr common.Address, slot common.Hash) (common
|
||||||
// existent.
|
// existent.
|
||||||
//
|
//
|
||||||
// An error will be returned if the state is corrupted in the underlying reader.
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
func (r *stateReaderWithCache) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
value, _, err := r.storage(addr, slot)
|
value, _, err := r.storage(addr, slot)
|
||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type readerWithCacheStats struct {
|
type readerWithStats struct {
|
||||||
*readerWithCache
|
*stateReaderWithCache
|
||||||
|
ContractCodeReaderWithStats
|
||||||
|
|
||||||
accountCacheHit atomic.Int64
|
accountCacheHit atomic.Int64
|
||||||
accountCacheMiss atomic.Int64
|
accountCacheMiss atomic.Int64
|
||||||
|
|
@ -581,10 +609,11 @@ type readerWithCacheStats struct {
|
||||||
storageCacheMiss atomic.Int64
|
storageCacheMiss atomic.Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// newReaderWithCacheStats constructs the reader with additional statistics tracked.
|
// newReaderWithStats constructs the reader with additional statistics tracked.
|
||||||
func newReaderWithCacheStats(reader *readerWithCache) *readerWithCacheStats {
|
func newReaderWithStats(sr *stateReaderWithCache, cr ContractCodeReaderWithStats) *readerWithStats {
|
||||||
return &readerWithCacheStats{
|
return &readerWithStats{
|
||||||
readerWithCache: reader,
|
stateReaderWithCache: sr,
|
||||||
|
ContractCodeReaderWithStats: cr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -592,8 +621,8 @@ func newReaderWithCacheStats(reader *readerWithCache) *readerWithCacheStats {
|
||||||
// The returned account might be nil if it's not existent.
|
// The returned account might be nil if it's not existent.
|
||||||
//
|
//
|
||||||
// An error will be returned if the state is corrupted in the underlying reader.
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
func (r *readerWithCacheStats) Account(addr common.Address) (*types.StateAccount, error) {
|
func (r *readerWithStats) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
account, incache, err := r.readerWithCache.account(addr)
|
account, incache, err := r.stateReaderWithCache.account(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -610,8 +639,8 @@ func (r *readerWithCacheStats) Account(addr common.Address) (*types.StateAccount
|
||||||
// existent.
|
// existent.
|
||||||
//
|
//
|
||||||
// An error will be returned if the state is corrupted in the underlying reader.
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
func (r *readerWithCacheStats) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
func (r *readerWithStats) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
value, incache, err := r.readerWithCache.storage(addr, slot)
|
value, incache, err := r.stateReaderWithCache.storage(addr, slot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -624,11 +653,14 @@ func (r *readerWithCacheStats) Storage(addr common.Address, slot common.Hash) (c
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStats implements ReaderWithStats, returning the statistics of state reader.
|
// GetStats implements ReaderWithStats, returning the statistics of state reader.
|
||||||
func (r *readerWithCacheStats) GetStats() ReaderStats {
|
func (r *readerWithStats) GetStats() ReaderStats {
|
||||||
|
codeHit, codeMiss := r.ContractCodeReaderWithStats.GetStats()
|
||||||
return ReaderStats{
|
return ReaderStats{
|
||||||
AccountCacheHit: r.accountCacheHit.Load(),
|
AccountCacheHit: r.accountCacheHit.Load(),
|
||||||
AccountCacheMiss: r.accountCacheMiss.Load(),
|
AccountCacheMiss: r.accountCacheMiss.Load(),
|
||||||
StorageCacheHit: r.storageCacheHit.Load(),
|
StorageCacheHit: r.storageCacheHit.Load(),
|
||||||
StorageCacheMiss: r.storageCacheMiss.Load(),
|
StorageCacheMiss: r.storageCacheMiss.Load(),
|
||||||
|
ContractCodeHit: codeHit,
|
||||||
|
ContractCodeMiss: codeMiss,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue