vm: simplify access to context, don't call Context() twice

Avoid going through Value(), avoid doing type casts twice for every
instruction.

name                    old time/op    new time/op    delta
ScriptFibonacci-8          793µs ± 3%     736µs ± 1%  -7.18%  (p=0.000 n=10+9)
ScriptNestedRefCount-8    1.09ms ± 1%    1.08ms ± 2%  -0.96%  (p=0.035 n=10+10)
ScriptPushPop/4-8         1.51µs ± 3%    1.48µs ± 3%    ~     (p=0.072 n=10+10)
ScriptPushPop/16-8        3.76µs ± 1%    3.59µs ± 1%  -4.56%  (p=0.000 n=10+10)
ScriptPushPop/128-8       25.0µs ± 1%    23.7µs ± 1%  -5.28%  (p=0.000 n=10+10)
ScriptPushPop/1024-8       184µs ± 1%     176µs ± 2%  -4.22%  (p=0.000 n=9+9)
This commit is contained in:
Roman Khimov 2021-08-28 22:31:08 +03:00
parent e09a0f3969
commit bc31c97c32
2 changed files with 15 additions and 8 deletions

View file

@ -286,8 +286,7 @@ func (v *VM) getContextScriptHash(n int) util.Uint160 {
return util.Uint160{} return util.Uint160{}
} }
element := istack.Peek(n) element := istack.Peek(n)
ctxIface := element.Value() ctx := element.value.(*Context)
ctx := ctxIface.(*Context)
return ctx.ScriptHash() return ctx.ScriptHash()
} }

View file

@ -322,7 +322,7 @@ func (v *VM) Context() *Context {
if v.istack.Len() == 0 { if v.istack.Len() == 0 {
return nil return nil
} }
return v.istack.Peek(0).Value().(*Context) return v.istack.Peek(0).value.(*Context)
} }
// PopResult is used to pop the first item of the evaluation stack. This allows // PopResult is used to pop the first item of the evaluation stack. This allows
@ -360,6 +360,8 @@ func (v *VM) Ready() bool {
// Run starts the execution of the loaded program. // Run starts the execution of the loaded program.
func (v *VM) Run() error { func (v *VM) Run() error {
var ctx *Context
if !v.Ready() { if !v.Ready() {
v.state = FaultState v.state = FaultState
return errors.New("no program loaded") return errors.New("no program loaded")
@ -372,6 +374,7 @@ func (v *VM) Run() error {
} }
// HaltState (the default) or BreakState are safe to continue. // HaltState (the default) or BreakState are safe to continue.
v.state = NoneState v.state = NoneState
ctx = v.Context()
for { for {
switch { switch {
case v.state.HasFlag(FaultState): case v.state.HasFlag(FaultState):
@ -382,7 +385,7 @@ func (v *VM) Run() error {
// Normal exit from this loop. // Normal exit from this loop.
return nil return nil
case v.state == NoneState: case v.state == NoneState:
if err := v.Step(); err != nil { if err := v.step(ctx); err != nil {
return err return err
} }
default: default:
@ -390,7 +393,7 @@ func (v *VM) Run() error {
return errors.New("unknown state") return errors.New("unknown state")
} }
// check for breakpoint before executing the next instruction // check for breakpoint before executing the next instruction
ctx := v.Context() ctx = v.Context()
if ctx != nil && ctx.atBreakPoint() { if ctx != nil && ctx.atBreakPoint() {
v.state = BreakState v.state = BreakState
} }
@ -400,6 +403,11 @@ func (v *VM) Run() error {
// Step 1 instruction in the program. // Step 1 instruction in the program.
func (v *VM) Step() error { func (v *VM) Step() error {
ctx := v.Context() ctx := v.Context()
return v.step(ctx)
}
// step executes one instruction in given context.
func (v *VM) step(ctx *Context) error {
op, param, err := ctx.Next() op, param, err := ctx.Next()
if err != nil { if err != nil {
v.state = FaultState v.state = FaultState
@ -1285,7 +1293,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
case opcode.RET: case opcode.RET:
oldCtx := v.istack.Pop().Value().(*Context) oldCtx := v.istack.Pop().value.(*Context)
oldEstack := v.estack oldEstack := v.estack
v.unloadContext(oldCtx) v.unloadContext(oldCtx)
@ -1552,7 +1560,7 @@ func calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
func (v *VM) handleException() { func (v *VM) handleException() {
for pop := 0; pop < v.istack.Len(); pop++ { for pop := 0; pop < v.istack.Len(); pop++ {
ictxv := v.istack.Peek(pop) ictxv := v.istack.Peek(pop)
ictx := ictxv.Value().(*Context) ictx := ictxv.value.(*Context)
for j := 0; j < ictx.tryStack.Len(); j++ { for j := 0; j < ictx.tryStack.Len(); j++ {
e := ictx.tryStack.Peek(j) e := ictx.tryStack.Peek(j)
ectx := e.Value().(*exceptionHandlingContext) ectx := e.Value().(*exceptionHandlingContext)
@ -1562,7 +1570,7 @@ func (v *VM) handleException() {
continue continue
} }
for i := 0; i < pop; i++ { for i := 0; i < pop; i++ {
ctx := v.istack.Pop().Value().(*Context) ctx := v.istack.Pop().value.(*Context)
v.unloadContext(ctx) v.unloadContext(ctx)
} }
if ectx.State == eTry && ectx.HasCatch() { if ectx.State == eTry && ectx.HasCatch() {