interop/vm: make VM reusable and use on VM for all in-block execs
Avoid allocating again and again. Increases TPS by about 3%.
This commit is contained in:
parent
bdc6624c9d
commit
92c94f265c
3 changed files with 41 additions and 9 deletions
|
@ -1095,7 +1095,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
|||
close(aerdone)
|
||||
}()
|
||||
_ = cache.GetItemCtx() // Prime serialization context cache (it'll be reused by upper layer DAOs).
|
||||
aer, err := bc.runPersist(bc.contracts.GetPersistScript(), block, cache, trigger.OnPersist)
|
||||
aer, v, err := bc.runPersist(bc.contracts.GetPersistScript(), block, cache, trigger.OnPersist, nil)
|
||||
if err != nil {
|
||||
// Release goroutines, don't care about errors, we already have one.
|
||||
close(aerchan)
|
||||
|
@ -1107,7 +1107,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
|||
|
||||
for _, tx := range block.Transactions {
|
||||
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
|
||||
v := systemInterop.SpawnVM()
|
||||
systemInterop.ReuseVM(v)
|
||||
v.LoadScriptWithFlags(tx.Script, callflag.All)
|
||||
v.GasLimit = tx.SystemFee
|
||||
|
||||
|
@ -1143,7 +1143,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
|||
aerchan <- aer
|
||||
}
|
||||
|
||||
aer, err = bc.runPersist(bc.contracts.GetPostPersistScript(), block, cache, trigger.PostPersist)
|
||||
aer, _, err = bc.runPersist(bc.contracts.GetPostPersistScript(), block, cache, trigger.PostPersist, v)
|
||||
if err != nil {
|
||||
// Release goroutines, don't care about errors, we already have one.
|
||||
close(aerchan)
|
||||
|
@ -1278,14 +1278,18 @@ func (bc *Blockchain) IsExtensibleAllowed(u util.Uint160) bool {
|
|||
return n < len(us)
|
||||
}
|
||||
|
||||
func (bc *Blockchain) runPersist(script []byte, block *block.Block, cache *dao.Simple, trig trigger.Type) (*state.AppExecResult, error) {
|
||||
func (bc *Blockchain) runPersist(script []byte, block *block.Block, cache *dao.Simple, trig trigger.Type, v *vm.VM) (*state.AppExecResult, *vm.VM, error) {
|
||||
systemInterop := bc.newInteropContext(trig, cache, block, nil)
|
||||
v := systemInterop.SpawnVM()
|
||||
if v == nil {
|
||||
v = systemInterop.SpawnVM()
|
||||
} else {
|
||||
systemInterop.ReuseVM(v)
|
||||
}
|
||||
v.LoadScriptWithFlags(script, callflag.All)
|
||||
if err := systemInterop.Exec(); err != nil {
|
||||
return nil, fmt.Errorf("VM has failed: %w", err)
|
||||
return nil, v, fmt.Errorf("VM has failed: %w", err)
|
||||
} else if _, err := systemInterop.DAO.Persist(); err != nil {
|
||||
return nil, fmt.Errorf("can't save changes: %w", err)
|
||||
return nil, v, fmt.Errorf("can't save changes: %w", err)
|
||||
}
|
||||
return &state.AppExecResult{
|
||||
Container: block.Hash(), // application logs can be retrieved by block hash
|
||||
|
@ -1296,7 +1300,7 @@ func (bc *Blockchain) runPersist(script []byte, block *block.Block, cache *dao.S
|
|||
Stack: v.Estack().ToArray(),
|
||||
Events: systemInterop.Notifications,
|
||||
},
|
||||
}, nil
|
||||
}, v, nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.Simple,
|
||||
|
|
|
@ -326,12 +326,22 @@ func (ic *Context) SyscallHandler(_ *vm.VM, id uint32) error {
|
|||
// SpawnVM spawns a new VM with the specified gas limit and set context.VM field.
|
||||
func (ic *Context) SpawnVM() *vm.VM {
|
||||
v := vm.NewWithTrigger(ic.Trigger)
|
||||
ic.initVM(v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (ic *Context) initVM(v *vm.VM) {
|
||||
v.LoadToken = ic.LoadToken
|
||||
v.GasLimit = -1
|
||||
v.SyscallHandler = ic.SyscallHandler
|
||||
v.SetPriceGetter(ic.GetPrice)
|
||||
ic.VM = v
|
||||
return v
|
||||
}
|
||||
|
||||
// ReuseVM resets given VM and allows to reuse it in the current context.
|
||||
func (ic *Context) ReuseVM(v *vm.VM) {
|
||||
v.Reset(ic.Trigger)
|
||||
ic.initVM(v)
|
||||
}
|
||||
|
||||
// RegisterCancelFunc adds the given function to the list of functions to be called after the VM
|
||||
|
|
18
pkg/vm/vm.go
18
pkg/vm/vm.go
|
@ -122,6 +122,24 @@ func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
|
|||
v.getPrice = f
|
||||
}
|
||||
|
||||
// Reset allows to reuse existing VM for subsequent executions making them somewhat
|
||||
// more efficient. It reuses invocation and evaluation stacks as well as VM structure
|
||||
// itself.
|
||||
func (v *VM) Reset(t trigger.Type) {
|
||||
v.state = NoneState
|
||||
v.getPrice = nil
|
||||
v.istack.elems = v.istack.elems[:0]
|
||||
v.estack.elems = v.estack.elems[:0]
|
||||
v.uncaughtException = nil
|
||||
v.refs = 0
|
||||
v.gasConsumed = 0
|
||||
v.GasLimit = 0
|
||||
v.SyscallHandler = nil
|
||||
v.LoadToken = nil
|
||||
v.trigger = t
|
||||
v.invTree = nil
|
||||
}
|
||||
|
||||
// GasConsumed returns the amount of GAS consumed during execution.
|
||||
func (v *VM) GasConsumed() int64 {
|
||||
return v.gasConsumed
|
||||
|
|
Loading…
Reference in a new issue