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{}
}
element := istack.Peek(n)
ctxIface := element.Value()
ctx := ctxIface.(*Context)
ctx := element.value.(*Context)
return ctx.ScriptHash()
}

View file

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