Sfoglia il codice sorgente

cache bitmap and change the cache type of GetCode (#449)

* change cache type of GetCode from fastcache to lrucache

Signed-off-by: kyrie-yl <yl.on.the.way@gmail.com>

* add cache for contract code bitmap

Signed-off-by: kyrie-yl <yl.on.the.way@gmail.com>

* core/vm: rework jumpdest analysis benchmarks (#23499)

* core/vm: rework jumpdest analysis benchmarks

For BenchmarkJumpdestOpAnalysis use fixed code size of ~1.2MB
and classic benchmark loop.

* core/vm: clear bitvec in jumpdest analysis benchmark

Co-authored-by: Paweł Bylica <chfast@gmail.com>
kyrie-yl 4 anni fa
parent
commit
33aa77949f
3 ha cambiato i file con 47 aggiunte e 18 eliminazioni
  1. 18 10
      core/state/database.go
  2. 14 4
      core/vm/analysis_test.go
  3. 15 4
      core/vm/contract.go

+ 18 - 10
core/state/database.go

@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/VictoriaMetrics/fastcache"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/rawdb"
 	"github.com/ethereum/go-ethereum/ethdb"
@@ -134,22 +133,24 @@ func NewDatabase(db ethdb.Database) Database {
 // large memory cache.
 func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
 	csc, _ := lru.New(codeSizeCacheSize)
+	cc, _ := lru.New(codeCacheSize)
 	return &cachingDB{
 		db:            trie.NewDatabaseWithConfig(db, config),
 		codeSizeCache: csc,
-		codeCache:     fastcache.New(codeCacheSize),
+		codeCache:     cc,
 	}
 }
 
 func NewDatabaseWithConfigAndCache(db ethdb.Database, config *trie.Config) Database {
 	csc, _ := lru.New(codeSizeCacheSize)
+	cc, _ := lru.New(codeCacheSize)
 	atc, _ := lru.New(accountTrieCacheSize)
 	stc, _ := lru.New(storageTrieCacheSize)
 
 	database := &cachingDB{
 		db:               trie.NewDatabaseWithConfig(db, config),
 		codeSizeCache:    csc,
-		codeCache:        fastcache.New(codeCacheSize),
+		codeCache:        cc,
 		accountTrieCache: atc,
 		storageTrieCache: stc,
 	}
@@ -160,7 +161,7 @@ func NewDatabaseWithConfigAndCache(db ethdb.Database, config *trie.Config) Datab
 type cachingDB struct {
 	db               *trie.Database
 	codeSizeCache    *lru.Cache
-	codeCache        *fastcache.Cache
+	codeCache        *lru.Cache
 	accountTrieCache *lru.Cache
 	storageTrieCache *lru.Cache
 }
@@ -266,12 +267,16 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
 
 // ContractCode retrieves a particular contract's code.
 func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
-	if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
-		return code, nil
+	if cached, ok := db.codeCache.Get(codeHash.Bytes()); ok {
+		code := cached.([]byte)
+		if len(code) > 0 {
+			return code, nil
+		}
 	}
 	code := rawdb.ReadCode(db.db.DiskDB(), codeHash)
 	if len(code) > 0 {
-		db.codeCache.Set(codeHash.Bytes(), code)
+
+		db.codeCache.Add(codeHash.Bytes(), code)
 		db.codeSizeCache.Add(codeHash, len(code))
 		return code, nil
 	}
@@ -282,12 +287,15 @@ func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error
 // code can't be found in the cache, then check the existence with **new**
 // db scheme.
 func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) {
-	if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
-		return code, nil
+	if cached, ok := db.codeCache.Get(codeHash.Bytes()); ok {
+		code := cached.([]byte)
+		if len(code) >  0 {
+			return code, nil
+		}
 	}
 	code := rawdb.ReadCodeWithPrefix(db.db.DiskDB(), codeHash)
 	if len(code) > 0 {
-		db.codeCache.Set(codeHash.Bytes(), code)
+		db.codeCache.Add(codeHash.Bytes(), code)
 		db.codeSizeCache.Add(codeHash, len(code))
 		return code, nil
 	}

+ 14 - 4
core/vm/analysis_test.go

@@ -55,9 +55,12 @@ func TestJumpDestAnalysis(t *testing.T) {
 	}
 }
 
+const analysisCodeSize = 1200 * 1024
+
 func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
 	// 1.4 ms
-	code := make([]byte, 1200000)
+	code := make([]byte, analysisCodeSize)
+	bench.SetBytes(analysisCodeSize)
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
 		codeBitmap(code)
@@ -66,7 +69,8 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
 }
 func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
 	// 4 ms
-	code := make([]byte, 1200000)
+	code := make([]byte, analysisCodeSize)
+	bench.SetBytes(analysisCodeSize)
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
 		crypto.Keccak256Hash(code)
@@ -77,13 +81,19 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
 func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
 	var op OpCode
 	bencher := func(b *testing.B) {
-		code := make([]byte, 32*b.N)
+		code := make([]byte, analysisCodeSize)
+		b.SetBytes(analysisCodeSize)
 		for i := range code {
 			code[i] = byte(op)
 		}
 		bits := make(bitvec, len(code)/8+1+4)
 		b.ResetTimer()
-		codeBitmapInternal(code, bits)
+		for i := 0; i < b.N; i++ {
+			for j := range bits {
+				bits[j] = 0
+			}
+			codeBitmapInternal(code, bits)
+		}
 	}
 	for op = PUSH1; op <= PUSH32; op++ {
 		bench.Run(op.String(), bencher)

+ 15 - 4
core/vm/contract.go

@@ -19,10 +19,16 @@ package vm
 import (
 	"math/big"
 
+	lru "github.com/hashicorp/golang-lru"
+
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/holiman/uint256"
 )
 
+const codeBitmapCacheSize = 2000
+
+var codeBitmapCache, _ = lru.New(codeBitmapCacheSize)
+
 // ContractRef is a reference to the contract's backing object
 type ContractRef interface {
 	Address() common.Address
@@ -110,10 +116,15 @@ func (c *Contract) isCode(udest uint64) bool {
 		// Does parent context have the analysis?
 		analysis, exist := c.jumpdests[c.CodeHash]
 		if !exist {
-			// Do the analysis and save in parent context
-			// We do not need to store it in c.analysis
-			analysis = codeBitmap(c.Code)
-			c.jumpdests[c.CodeHash] = analysis
+			if cached, ok := codeBitmapCache.Get(c.CodeHash); ok {
+				analysis = cached.(bitvec)
+			} else {
+				// Do the analysis and save in parent context
+				// We do not need to store it in c.analysis
+				analysis = codeBitmap(c.Code)
+				c.jumpdests[c.CodeHash] = analysis
+				codeBitmapCache.Add(c.CodeHash, analysis)
+			}
 		}
 		// Also stash it in current contract for faster access
 		c.analysis = analysis