vm: optimize Next() in Context
Creating a new BinReader for every instruction is a bit too much and it adds about 1% overhead on block import (and actually is quite visible in the VM profiling statistics). So use a bit more ugly but efficient method.
This commit is contained in:
parent
a9401e2ec7
commit
c9257c3de4
1 changed files with 36 additions and 20 deletions
|
@ -1,10 +1,10 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
@ -36,6 +36,8 @@ type Context struct {
|
||||||
scriptHash util.Uint160
|
scriptHash util.Uint160
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errNoInstParam = errors.New("failed to read instruction parameter")
|
||||||
|
|
||||||
// NewContext returns a new Context object.
|
// NewContext returns a new Context object.
|
||||||
func NewContext(b []byte) *Context {
|
func NewContext(b []byte) *Context {
|
||||||
return &Context{
|
return &Context{
|
||||||
|
@ -49,33 +51,44 @@ func NewContext(b []byte) *Context {
|
||||||
// its invocation the instruction pointer points to the instruction being
|
// its invocation the instruction pointer points to the instruction being
|
||||||
// returned.
|
// returned.
|
||||||
func (c *Context) Next() (opcode.Opcode, []byte, error) {
|
func (c *Context) Next() (opcode.Opcode, []byte, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
c.ip = c.nextip
|
c.ip = c.nextip
|
||||||
if c.ip >= len(c.prog) {
|
if c.ip >= len(c.prog) {
|
||||||
return opcode.RET, nil, nil
|
return opcode.RET, nil, nil
|
||||||
}
|
}
|
||||||
r := io.NewBinReaderFromBuf(c.prog[c.ip:])
|
|
||||||
|
|
||||||
var instrbyte = r.ReadB()
|
var instrbyte = c.prog[c.ip]
|
||||||
instr := opcode.Opcode(instrbyte)
|
instr := opcode.Opcode(instrbyte)
|
||||||
c.nextip++
|
c.nextip++
|
||||||
|
|
||||||
var numtoread int
|
var numtoread int
|
||||||
switch instr {
|
switch instr {
|
||||||
case opcode.PUSHDATA1, opcode.SYSCALL:
|
case opcode.PUSHDATA1, opcode.SYSCALL:
|
||||||
var n = r.ReadB()
|
if c.nextip >= len(c.prog) {
|
||||||
numtoread = int(n)
|
err = errNoInstParam
|
||||||
c.nextip++
|
} else {
|
||||||
case opcode.PUSHDATA2:
|
numtoread = int(c.prog[c.nextip])
|
||||||
var n = r.ReadU16LE()
|
c.nextip++
|
||||||
numtoread = int(n)
|
}
|
||||||
c.nextip += 2
|
case opcode.PUSHDATA2:
|
||||||
case opcode.PUSHDATA4:
|
if c.nextip+1 >= len(c.prog) {
|
||||||
var n = r.ReadU32LE()
|
err = errNoInstParam
|
||||||
if n > MaxItemSize {
|
} else {
|
||||||
return instr, nil, errors.New("parameter is too big")
|
numtoread = int(binary.LittleEndian.Uint16(c.prog[c.nextip : c.nextip+2]))
|
||||||
|
c.nextip += 2
|
||||||
|
}
|
||||||
|
case opcode.PUSHDATA4:
|
||||||
|
if c.nextip+3 >= len(c.prog) {
|
||||||
|
err = errNoInstParam
|
||||||
|
} else {
|
||||||
|
var n = binary.LittleEndian.Uint32(c.prog[c.nextip : c.nextip+4])
|
||||||
|
if n > MaxItemSize {
|
||||||
|
return instr, nil, errors.New("parameter is too big")
|
||||||
|
}
|
||||||
|
numtoread = int(n)
|
||||||
|
c.nextip += 4
|
||||||
}
|
}
|
||||||
numtoread = int(n)
|
|
||||||
c.nextip += 4
|
|
||||||
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL, opcode.CALLED, opcode.CALLEDT:
|
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL, opcode.CALLED, opcode.CALLEDT:
|
||||||
numtoread = 2
|
numtoread = 2
|
||||||
case opcode.CALLI:
|
case opcode.CALLI:
|
||||||
|
@ -92,11 +105,14 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
|
||||||
return instr, nil, nil
|
return instr, nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parameter := make([]byte, numtoread)
|
if c.nextip+numtoread-1 >= len(c.prog) {
|
||||||
r.ReadBytes(parameter)
|
err = errNoInstParam
|
||||||
if r.Err != nil {
|
|
||||||
return instr, nil, errors.New("failed to read instruction parameter")
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return instr, nil, err
|
||||||
|
}
|
||||||
|
parameter := make([]byte, numtoread)
|
||||||
|
copy(parameter, c.prog[c.nextip:c.nextip+numtoread])
|
||||||
c.nextip += numtoread
|
c.nextip += numtoread
|
||||||
return instr, parameter, nil
|
return instr, parameter, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue