stackitem: don't copy existing slices for TryBytes

Most often we only need to read them and it doesn't require copying. Make an
explicit copy (and copy only things we need!) where needed.

After the recent neo-vm tests update our vm package testing time jumped to
~12s, with this change it's now more like ~8s.
This commit is contained in:
Roman Khimov 2020-08-22 23:36:38 +03:00
parent 77ea3d361b
commit 324f4c265b
2 changed files with 22 additions and 13 deletions

View file

@ -33,7 +33,8 @@ type Item interface {
Dup() Item Dup() Item
// TryBool converts Item to a boolean value. // TryBool converts Item to a boolean value.
TryBool() (bool, error) TryBool() (bool, error)
// TryBytes converts Item to a byte slice. // TryBytes converts Item to a byte slice. If the underlying type is a
// byte slice, it's returned as is without copying.
TryBytes() ([]byte, error) TryBytes() ([]byte, error)
// TryInteger converts Item to an integer. // TryInteger converts Item to an integer.
TryInteger() (*big.Int, error) TryInteger() (*big.Int, error)
@ -151,8 +152,11 @@ func convertPrimitive(item Item, typ Type) (Item, error) {
return nil, err return nil, err
} }
if typ == BufferT { if typ == BufferT {
return NewBuffer(b), nil newb := make([]byte, len(b))
copy(newb, b)
return NewBuffer(newb), nil
} }
// ByteArray can't really be changed, so it's OK to reuse `b`.
return NewByteArray(b), nil return NewByteArray(b), nil
case BooleanT: case BooleanT:
b, err := item.TryBool() b, err := item.TryBool()
@ -519,9 +523,7 @@ func (i *ByteArray) TryBool() (bool, error) {
// TryBytes implements Item interface. // TryBytes implements Item interface.
func (i *ByteArray) TryBytes() ([]byte, error) { func (i *ByteArray) TryBytes() ([]byte, error) {
val := make([]byte, len(i.value)) return i.value, nil
copy(val, i.value)
return val, nil
} }
// TryInteger implements Item interface. // TryInteger implements Item interface.
@ -982,9 +984,7 @@ func (i *Buffer) TryBool() (bool, error) {
// TryBytes implements Item interface. // TryBytes implements Item interface.
func (i *Buffer) TryBytes() ([]byte, error) { func (i *Buffer) TryBytes() ([]byte, error) {
val := make([]byte, len(i.value)) return i.value, nil
copy(val, i.value)
return val, nil
} }
// TryInteger implements Item interface. // TryInteger implements Item interface.

View file

@ -663,10 +663,13 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.CAT: case opcode.CAT:
b := v.estack.Pop().Bytes() b := v.estack.Pop().Bytes()
a := v.estack.Pop().Bytes() a := v.estack.Pop().Bytes()
if l := len(a) + len(b); l > stackitem.MaxSize { l := len(a) + len(b)
if l > stackitem.MaxSize {
panic(fmt.Sprintf("too big item: %d", l)) panic(fmt.Sprintf("too big item: %d", l))
} }
ab := append(a, b...) ab := make([]byte, l)
copy(ab, a)
copy(ab[len(a):], b)
v.estack.PushVal(stackitem.NewBuffer(ab)) v.estack.PushVal(stackitem.NewBuffer(ab))
case opcode.SUBSTR: case opcode.SUBSTR:
@ -683,7 +686,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if last > len(s) { if last > len(s) {
panic("invalid offset") panic("invalid offset")
} }
v.estack.PushVal(stackitem.NewBuffer(s[o:last])) res := make([]byte, l)
copy(res, s[o:last])
v.estack.PushVal(stackitem.NewBuffer(res))
case opcode.LEFT: case opcode.LEFT:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -694,7 +699,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if t := len(s); l > t { if t := len(s); l > t {
panic("size is too big") panic("size is too big")
} }
v.estack.PushVal(stackitem.NewBuffer(s[:l])) res := make([]byte, l)
copy(res, s[:l])
v.estack.PushVal(stackitem.NewBuffer(res))
case opcode.RIGHT: case opcode.RIGHT:
l := int(v.estack.Pop().BigInt().Int64()) l := int(v.estack.Pop().BigInt().Int64())
@ -702,7 +709,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("negative length") panic("negative length")
} }
s := v.estack.Pop().Bytes() s := v.estack.Pop().Bytes()
v.estack.PushVal(stackitem.NewBuffer(s[len(s)-l:])) res := make([]byte, l)
copy(res, s[len(s)-l:])
v.estack.PushVal(stackitem.NewBuffer(res))
case opcode.DEPTH: case opcode.DEPTH:
v.estack.PushVal(v.estack.Len()) v.estack.PushVal(v.estack.Len())