Skip to content

Commit

Permalink
Merge pull request #2106 from nspcc-dev/microopt
Browse files Browse the repository at this point in the history
Microoptimizations
  • Loading branch information
roman-khimov authored Aug 3, 2021
2 parents dfc514e + 64c780a commit 5e18a61
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 61 deletions.
4 changes: 2 additions & 2 deletions pkg/core/fee/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
func Opcode(base int64, opcodes ...opcode.Opcode) int64 {
var result int64
for _, op := range opcodes {
result += coefficients[op]
result += int64(coefficients[op])
}
return result * base
}

var coefficients = map[opcode.Opcode]int64{
var coefficients = [256]uint16{
opcode.PUSHINT8: 1 << 0,
opcode.PUSHINT16: 1 << 0,
opcode.PUSHINT32: 1 << 0,
Expand Down
19 changes: 19 additions & 0 deletions pkg/core/fee/opcode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package fee

import (
"testing"

"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
)

const feeFactor = 30

// The most common Opcode() use case is to get price for single opcode.
func BenchmarkOpcode1(t *testing.B) {
// Just so that we don't always test the same opcode.
script := []opcode.Opcode{opcode.NOP, opcode.ADD, opcode.SYSCALL, opcode.APPEND}
l := len(script)
for n := 0; n < t.N; n++ {
_ = Opcode(feeFactor, script[n%l])
}
}
11 changes: 8 additions & 3 deletions pkg/core/native/native_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ func newGAS(init int64) *GAS {
return g
}

func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error {
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) error {
acc, err := state.NEP17BalanceFromBytes(*si)
if err != nil {
return err
}
if sign := amount.Sign(); sign == 0 {
return nil
// Requested self-transfer amount can be higher than actual balance.
if checkBal != nil && acc.Balance.Cmp(checkBal) < 0 {
err = errors.New("insufficient funds")
}
return err
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds")
}
Expand Down Expand Up @@ -104,7 +108,8 @@ func (g *GAS) Initialize(ic *interop.Context) error {
if err := g.nep17TokenNative.Initialize(ic); err != nil {
return err
}
if g.nep17TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
_, totalSupply := g.nep17TokenNative.getTotalSupply(ic.DAO)
if totalSupply.Sign() != 0 {
return errors.New("already initialized")
}
h, err := getStandbyValidatorsHash(ic)
Expand Down
11 changes: 7 additions & 4 deletions pkg/core/native/native_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ func (n *NEO) Initialize(ic *interop.Context) error {
return err
}

if n.nep17TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
_, totalSupply := n.nep17TokenNative.getTotalSupply(ic.DAO)
if totalSupply.Sign() != 0 {
return errors.New("already initialized")
}

Expand Down Expand Up @@ -391,12 +392,13 @@ func (n *NEO) getGASPerVote(d dao.DAO, key []byte, index ...uint32) []big.Int {
return reward
}

func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) error {
acc, err := state.NEOBalanceFromBytes(*si)
if err != nil {
return err
}
if amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
if (amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1) ||
(amount.Sign() == 0 && checkBal != nil && acc.Balance.Cmp(checkBal) == -1) {
return errors.New("insufficient funds")
}
if err := n.distributeGas(ic, h, acc); err != nil {
Expand Down Expand Up @@ -976,7 +978,8 @@ func (n *NEO) computeCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (
votersCount := bigint.FromBytes(si)
// votersCount / totalSupply must be >= 0.2
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
voterTurnout := votersCount.Div(votersCount, n.getTotalSupply(d))
_, totalSupply := n.getTotalSupply(d)
voterTurnout := votersCount.Div(votersCount, totalSupply)

sbVals := bc.GetStandByCommittee()
count := len(sbVals)
Expand Down
44 changes: 14 additions & 30 deletions pkg/core/native/native_nep17.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type nep17TokenNative struct {
symbol string
decimals int64
factor int64
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int, *big.Int) error
balFromBytes func(item *state.StorageItem) (*big.Int, error)
}

Expand Down Expand Up @@ -95,19 +95,21 @@ func (c *nep17TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stac
}

func (c *nep17TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
return stackitem.NewBigInteger(c.getTotalSupply(ic.DAO))
_, supply := c.getTotalSupply(ic.DAO)
return stackitem.NewBigInteger(supply)
}

func (c *nep17TokenNative) getTotalSupply(d dao.DAO) *big.Int {
func (c *nep17TokenNative) getTotalSupply(d dao.DAO) (state.StorageItem, *big.Int) {
si := d.GetStorageItem(c.ID, totalSupplyKey)
if si == nil {
return big.NewInt(0)
si = []byte{}
}
return bigint.FromBytes(si)
return si, bigint.FromBytes(si)
}

func (c *nep17TokenNative) saveTotalSupply(d dao.DAO, supply *big.Int) error {
return d.PutStorageItem(c.ID, totalSupplyKey, bigint.ToBytes(supply))
func (c *nep17TokenNative) saveTotalSupply(d dao.DAO, si state.StorageItem, supply *big.Int) error {
si = state.StorageItem(bigint.ToPreallocatedBytes(supply, si))
return d.PutStorageItem(c.ID, totalSupplyKey, si)
}

func (c *nep17TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
Expand Down Expand Up @@ -173,29 +175,11 @@ func (c *nep17TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint16
return errors.New("insufficient funds")
}
si = state.StorageItem{}
} else if amount.Sign() == 0 && requiredBalance != nil {
// If amount == 0 then it's either a round trip or an empty transfer. In
// case of a round trip account's balance may still be less than actual
// transfer's amount, so we need to check it. Other cases are handled by
// `incBalance` method.
balance, err := c.balFromBytes(&si)
if err != nil {
return fmt.Errorf("failed to deserialise balance: %w", err)
}
if balance.Cmp(requiredBalance) < 0 {
// Firstly, need to put it back to storage as it affects dumps.
err = ic.DAO.PutStorageItem(c.ID, key, si)
if err != nil {
return err
}
// Finally, return an error.
return errors.New("insufficient funds")
}
}

err := c.incBalance(ic, acc, &si, amount)
err := c.incBalance(ic, acc, &si, amount, requiredBalance)
if err != nil {
if si != nil && amount.Sign() < 0 {
if si != nil && amount.Sign() <= 0 {
_ = ic.DAO.PutStorageItem(c.ID, key, si)
}
return err
Expand Down Expand Up @@ -288,7 +272,7 @@ func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
if si == nil {
si = state.StorageItem{}
}
if err := c.incBalance(ic, h, &si, amount); err != nil {
if err := c.incBalance(ic, h, &si, amount, nil); err != nil {
panic(err)
}
var err error
Expand All @@ -301,9 +285,9 @@ func (c *nep17TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
panic(err)
}

supply := c.getTotalSupply(ic.DAO)
buf, supply := c.getTotalSupply(ic.DAO)
supply.Add(supply, amount)
err = c.saveTotalSupply(ic.DAO, supply)
err = c.saveTotalSupply(ic.DAO, buf, supply)
if err != nil {
panic(err)
}
Expand Down
15 changes: 9 additions & 6 deletions pkg/core/state/native_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,18 @@ func (s *NEOBalance) Bytes() []byte {

// ToStackItem implements stackitem.Convertible interface. It never returns an error.
func (s *NEOBalance) ToStackItem() (stackitem.Item, error) {
resItem, _ := s.NEP17Balance.ToStackItem()
result := resItem.(*stackitem.Struct)
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
var voteItem stackitem.Item

if s.VoteTo != nil {
result.Append(stackitem.NewByteArray(s.VoteTo.Bytes()))
voteItem = stackitem.NewByteArray(s.VoteTo.Bytes())
} else {
result.Append(stackitem.Null{})
voteItem = stackitem.Null{}
}
return result, nil
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(&s.Balance),
stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))),
voteItem,
}), nil
}

// FromStackItem converts stackitem.Item to NEOBalance.
Expand Down
15 changes: 15 additions & 0 deletions pkg/vm/opcode/isvalid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package opcode

import (
"testing"
)

// IsValid() is called for every VM instruction.
func BenchmarkIsValid(t *testing.B) {
// Just so that we don't always test the same opcode.
script := []Opcode{NOP, ADD, SYSCALL, APPEND, 0xff, 0xf0}
l := len(script)
for n := 0; n < t.N; n++ {
_ = IsValid(script[n%l])
}
}
12 changes: 10 additions & 2 deletions pkg/vm/opcode/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,16 @@ const (
CONVERT Opcode = 0xDB
)

var validCodes [256]bool

func init() {
// We rely on stringer here, it has a map anyway.
for op := range _Opcode_map {
validCodes[int(op)] = true
}
}

// IsValid returns true if the opcode passed is valid (defined in the VM).
func IsValid(op Opcode) bool {
_, ok := _Opcode_map[op] // We rely on stringer here, it has a map anyway.
return ok
return validCodes[int(op)]
}
6 changes: 6 additions & 0 deletions pkg/vm/ref_counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func newRefCounter() *refCounter {

// Add adds an item to the reference counter.
func (r *refCounter) Add(item stackitem.Item) {
if r == nil {
return
}
r.size++

switch item.(type) {
Expand All @@ -41,6 +44,9 @@ func (r *refCounter) Add(item stackitem.Item) {

// Remove removes item from the reference counter.
func (r *refCounter) Remove(item stackitem.Item) {
if r == nil {
return
}
r.size--

switch item.(type) {
Expand Down
7 changes: 5 additions & 2 deletions pkg/vm/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,16 @@ type Stack struct {

// NewStack returns a new stack name by the given name.
func NewStack(n string) *Stack {
return newStack(n, newRefCounter())
}

func newStack(n string, refc *refCounter) *Stack {
s := &Stack{
name: n,
refs: refc,
}
s.top.next = &s.top
s.top.prev = &s.top
s.len = 0
s.refs = newRefCounter()
return s
}

Expand Down
17 changes: 5 additions & 12 deletions pkg/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,25 +99,18 @@ func New() *VM {
func NewWithTrigger(t trigger.Type) *VM {
vm := &VM{
state: NoneState,
istack: NewStack("invocation"),
istack: newStack("invocation", nil),
refs: newRefCounter(),
trigger: t,

SyscallHandler: defaultSyscallHandler,
Invocations: make(map[util.Uint160]int),
}

vm.estack = vm.newItemStack("evaluation")
vm.estack = newStack("evaluation", vm.refs)
return vm
}

func (v *VM) newItemStack(n string) *Stack {
s := NewStack(n)
s.refs = v.refs

return s
}

// SetPriceGetter registers the given PriceGetterFunc in v.
// f accepts vm's Context, current instruction and instruction parameter.
func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
Expand Down Expand Up @@ -288,9 +281,9 @@ func (v *VM) LoadScript(b []byte) {
func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
v.checkInvocationStackSize()
ctx := NewContextWithParams(b, 0, -1, 0)
v.estack = v.newItemStack("estack")
v.estack = newStack("evaluation", v.refs)
ctx.estack = v.estack
ctx.tryStack = NewStack("exception")
ctx.tryStack = newStack("exception", nil)
ctx.callFlag = f
ctx.static = newSlot(v.refs)
ctx.callingScriptHash = v.GetCurrentScriptHash()
Expand Down Expand Up @@ -1527,7 +1520,7 @@ func (v *VM) call(ctx *Context, offset int) {
newCtx.RetCount = -1
newCtx.local = nil
newCtx.arguments = nil
newCtx.tryStack = NewStack("exception")
newCtx.tryStack = newStack("exception", nil)
newCtx.NEF = ctx.NEF
v.istack.PushVal(newCtx)
v.Jump(newCtx, offset)
Expand Down

0 comments on commit 5e18a61

Please sign in to comment.