Skip to content

Commit

Permalink
Merge pull request bnb-chain#5 from redhdx/feature/alpha_refine
Browse files Browse the repository at this point in the history
feature(op-geth): code refine
  • Loading branch information
sunny2022da committed Mar 20, 2024
2 parents bbc6612 + 1136f0d commit 9c5127e
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 192 deletions.
142 changes: 13 additions & 129 deletions core/opcodeCompiler/compiler/OpCodeCache.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
package compiler

import (
"encoding/json"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/log"
"os"
"os/signal"
"sync"
"syscall"
)

// CodeCacheGCThreshold TODO: make codecache threshold configurable.
const CodeCacheGCThreshold = 1024 * 1024 * 1024 /* 1 GB */
// CodeCacheGCSoftLimit is used to trigger GC for memory control.
// upper limit of bytecodes of smart contract is ~25MB.
const CodeCacheGCSoftLimit = 200 * 1024 * 1024 /* 200MB */

type OptCode []byte

type OpCodeCache struct {
opcodesCache map[common.Hash]OptCode
bitvecCache *lru.SizeConstrainedCache[common.Hash, []byte]
codeCacheMutex sync.RWMutex
codeCacheSize uint64
optimizedCodeCache *lru.Cache[common.Hash, []byte]
bitvecCache *lru.Cache[common.Hash, []byte]
}

func (c *OpCodeCache) GetBitvecCache(codeHash common.Hash) []byte {
bitvec, ok := c.bitvecCache.Get(codeHash)
if !ok {
bitvec = nil
}
func (c *OpCodeCache) GetCachedBitvec(codeHash common.Hash) []byte {
bitvec, _ := c.bitvecCache.Get(codeHash)
return bitvec
}

Expand All @@ -39,129 +20,32 @@ func (c *OpCodeCache) AddBitvecCache(codeHash common.Hash, bitvec []byte) {
}

func (c *OpCodeCache) RemoveCachedCode(hash common.Hash) {
c.codeCacheMutex.Lock()
if c.opcodesCache == nil || c.codeCacheSize == 0 {
c.codeCacheMutex.Unlock()
return
}
_, ok := c.opcodesCache[hash]
if ok {
delete(c.opcodesCache, hash)
}
c.codeCacheMutex.Unlock()
c.optimizedCodeCache.Remove(hash)
}

func (c *OpCodeCache) GetCachedCode(hash common.Hash) OptCode {
c.codeCacheMutex.RLock()
processedCode, ok := c.opcodesCache[hash]
if !ok {
processedCode = nil
}
c.codeCacheMutex.RUnlock()
func (c *OpCodeCache) GetCachedCode(hash common.Hash) []byte {
processedCode, _ := c.optimizedCodeCache.Get(hash)
return processedCode
}

func (c *OpCodeCache) UpdateCodeCache(hash common.Hash, code OptCode) error {
c.codeCacheMutex.Lock()

if c.codeCacheSize+CodeCacheGCSoftLimit > CodeCacheGCThreshold {
log.Warn("Code cache GC triggered\n")
// TODO: should we depends on Golang GC here?
// TODO: the current implementation of clear all is not reasonable.
// must have better algorithm such as LRU and should consider hot addresses such as ones in accesslist.
for k := range c.opcodesCache {
delete(c.opcodesCache, k)
}
c.codeCacheSize = 0
}

c.opcodesCache[hash] = code
c.codeCacheSize += uint64(len(code))
c.codeCacheMutex.Unlock()
return nil
func (c *OpCodeCache) AddCodeCache(hash common.Hash, optimizedCode []byte) {
c.optimizedCodeCache.Add(hash, optimizedCode)
}

var opcodeCache *OpCodeCache

const (
codeCacheFileName = "codecache.json"
bitvecCacheSize = 64 * 1024 * 1024
optimizedCodeCacheCap = 1024
bitvecCacheCap = 1024
)

func init() {
opcodeCache = &OpCodeCache{
opcodesCache: make(map[common.Hash]OptCode, CodeCacheGCThreshold>>10),
bitvecCache: lru.NewSizeConstrainedCache[common.Hash, []byte](bitvecCacheSize),
codeCacheMutex: sync.RWMutex{},
optimizedCodeCache: lru.NewCache[common.Hash, []byte](optimizedCodeCacheCap),
bitvecCache: lru.NewCache[common.Hash, []byte](bitvecCacheCap),
}

// Try load code cache
loadCodeCacheFromFile(codeCacheFileName, opcodeCache)
// Handle Sigusr2 signal
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGUSR2)
go func() {
for { // Infinite loop to wait for signals
signal := <-sigCh
switch signal {
case syscall.SIGUSR2:
opcodeCache.codeCacheMutex.RLock()
dumpCodeCache(codeCacheFileName, opcodeCache.opcodesCache)
opcodeCache.codeCacheMutex.RUnlock()
}
}
}()
}

func getOpCodeCacheInstance() *OpCodeCache {
return opcodeCache
}

func dumpCodeCache(filename string, codeCache map[common.Hash]OptCode) {

// Marshal data to JSON
jsonData, err := json.MarshalIndent(codeCache, "", " ")
if err != nil {
log.Error("Error marshaling codecache to JSON:", "err", err)
return
}

log.Info("OpcodeCache Dump:", "File", filename)
// Optional: write JSON to file
err = writeToFile(filename, jsonData)
if err != nil {
log.Error("Error writing JSON file:", "err", err)
}
}

func writeToFile(filename string, data []byte) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()

size, err := f.Write(data)
if err != nil {
log.Warn("Fail dump data", "error", err)
return err
}
log.Warn("dump data: ", "File", filename, "Size:", size)
return err
}

func loadCodeCacheFromFile(filename string, cacheInstance *OpCodeCache) {
data, err := os.ReadFile(filename)
if err != nil {
log.Warn("Fail to load Code Cache", "File", filename, "error", err)
return
}

err = json.Unmarshal(data, &cacheInstance.opcodesCache)
if err != nil {
log.Warn("Fail to load Code Cache", "File", filename, "error", err)
return
}
log.Info("Load Code Cache success", "File", filename, "Size", len(data))
return
}
88 changes: 28 additions & 60 deletions core/opcodeCompiler/compiler/opcodeProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,40 @@ import (
"runtime"
)

type CodeType uint8

var ErrFailPreprocessing = errors.New("fail to do preprocessing")
var ErrOptiDisabled = errors.New("Opcode optimization is disabled")

var opCodeOptimizationInited bool

const taskChannelSize = 1024 * 1024

var (
enabled bool
codeCache *OpCodeCache
taskChannel chan optimizeTask
)

type OpCodeProcessorConfig struct {
DoOpcodeFusion bool
}
var (
ErrFailPreprocessing = errors.New("fail to do preprocessing")
ErrOptimizedDisabled = errors.New("opcode optimization is disabled")
)

type optimizeTaskType byte
const taskChannelSize = 1024 * 1024

const (
unknown optimizeTaskType = 0
generate optimizeTaskType = 1
flush optimizeTaskType = 2
)

type OpCodeProcessorConfig struct {
DoOpcodeFusion bool
}

type optimizeTaskType byte

type CodeType uint8

type optimizeTask struct {
taskType optimizeTaskType
hash common.Hash
rawCode []byte
}

func init() {
if opCodeOptimizationInited {
return
}
opCodeOptimizationInited = true
enabled = false
codeCache = nil
taskChannel = make(chan optimizeTask, taskChannelSize)
// start task processors.
taskNumber := runtime.NumCPU() * 3 / 8
if taskNumber < 1 {
taskNumber = 1
Expand All @@ -72,12 +64,10 @@ func DisableOptimization() {
enabled = false
}

// Producer functions
func LoadOptimizedCode(hash common.Hash) OptCode {
func LoadOptimizedCode(hash common.Hash) []byte {
if !enabled {
return nil
}
/* Try load from cache */
processedCode := codeCache.GetCachedCode(hash)
return processedCode

Expand All @@ -87,7 +77,7 @@ func LoadBitvec(codeHash common.Hash) []byte {
if !enabled {
return nil
}
bitvec := codeCache.GetBitvecCache(codeHash)
bitvec := codeCache.GetCachedBitvec(codeHash)
return bitvec
}

Expand All @@ -106,21 +96,6 @@ func GenOrLoadOptimizedCode(hash common.Hash, code []byte) {
taskChannel <- task
}

func FlushCodeCache(hash common.Hash) {
if !enabled {
return
}
task := optimizeTask{flush, hash, nil}
taskChannel <- task
}

func RewriteOptimizedCodeForDB(hash common.Hash, code []byte) {
if enabled {
GenOrLoadOptimizedCode(hash, code)
}
}

// Consumer function
func taskProcessor() {
for {
task := <-taskChannel
Expand All @@ -132,45 +107,38 @@ func taskProcessor() {
func handleOptimizationTask(task optimizeTask) {
switch task.taskType {
case generate:
TryGenerateOptimizedCode(task.hash, task.rawCode)
_, err := TryGenerateOptimizedCode(task.hash, task.rawCode)
if err != nil {
log.Error("Can not generate optimized code", "error", err)
}
case flush:
DeleteCodeCache(task.hash)
}
}

// GenOrRewriteOptimizedCode generate the optimized code and refresh the codecache.
func GenOrRewriteOptimizedCode(hash common.Hash, code []byte) (OptCode, error) {
// GenOrRewriteOptimizedCode generate the optimized code and refresh the code cache.
func GenOrRewriteOptimizedCode(hash common.Hash, code []byte) ([]byte, error) {
if !enabled {
return nil, ErrOptiDisabled
return nil, ErrOptimizedDisabled
}
processedCode, err := processByteCodes(code)
if err != nil {
log.Error("Can not generate optimized code: %s\n", err.Error())
return nil, err
}

err = codeCache.UpdateCodeCache(hash, processedCode)
if err != nil {
log.Error("Not update code cache", "err", err)
}
codeCache.AddCodeCache(hash, processedCode)
return processedCode, err
}

func TryGenerateOptimizedCode(hash common.Hash, code []byte) (OptCode, bool, error) {
func TryGenerateOptimizedCode(hash common.Hash, code []byte) ([]byte, error) {
if !enabled {
return nil, false, ErrOptiDisabled
return nil, ErrOptimizedDisabled
}
/* Try load from cache */
processedCode := codeCache.GetCachedCode(hash)
hit := false
var err error = nil
if processedCode == nil || len(processedCode) == 0 {
processedCode, err = GenOrRewriteOptimizedCode(hash, code)
hit = false
} else {
hit = true
}
return processedCode, hit, err
return processedCode, err
}

func DeleteCodeCache(hash common.Hash) {
Expand All @@ -181,11 +149,11 @@ func DeleteCodeCache(hash common.Hash) {
codeCache.RemoveCachedCode(hash)
}

func processByteCodes(code []byte) (OptCode, error) {
func processByteCodes(code []byte) ([]byte, error) {
return doOpcodesProcess(code)
}

func doOpcodesProcess(code []byte) (OptCode, error) {
func doOpcodesProcess(code []byte) ([]byte, error) {
code, err := doCodeFusion(code)
if err != nil {
return nil, ErrFailPreprocessing
Expand Down
1 change: 0 additions & 1 deletion core/vm/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,6 @@ func TestRuntimeJSTracerWithOpcodeOptimizer(t *testing.T) {
Tracer: tracer,
EnableOpcodeOptimizations: true,
}})
compiler.DeleteCodeCache(main)
if err != nil {
t.Fatal("didn't expect error", err)
}
Expand Down
4 changes: 2 additions & 2 deletions eth/tracers/js/tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ func runTraceWithOption(tracer tracers.Tracer, vmctx *vmContext, chaincfg *param

if enableOpti {
// reset the code also require flush code cache.
compiler.DeleteCodeCache(contract.Address())
optimized, _ := compiler.GenOrRewriteOptimizedCode(contract.Address(), contract.Code)
compiler.DeleteCodeCache(contract.CodeHash)
optimized, _ := compiler.GenOrRewriteOptimizedCode(contract.CodeHash, contract.Code)
contract.Code = optimized
}

Expand Down

0 comments on commit 9c5127e

Please sign in to comment.