forked from cerc-io/plugeth
core/vm: reuse bigint pools across transactions (#17070)
* core/vm: A pool for int pools * core/vm: fix rebase issue * core/vm: push leftover stack items after execution, not before
This commit is contained in:
parent
d57e85ecc9
commit
4e5d1f1c39
@ -36,6 +36,7 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
|
|||||||
stack = newstack()
|
stack = newstack()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
|
env.interpreter.intPool = poolOfIntPools.get()
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
x := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
|
x := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
|
||||||
shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y))
|
shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y))
|
||||||
@ -64,6 +65,7 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
poolOfIntPools.put(env.interpreter.intPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestByteOp(t *testing.T) {
|
func TestByteOp(t *testing.T) {
|
||||||
@ -71,6 +73,7 @@ func TestByteOp(t *testing.T) {
|
|||||||
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
|
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
)
|
)
|
||||||
|
env.interpreter.intPool = poolOfIntPools.get()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
v string
|
v string
|
||||||
th uint64
|
th uint64
|
||||||
@ -97,6 +100,7 @@ func TestByteOp(t *testing.T) {
|
|||||||
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual)
|
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
poolOfIntPools.put(env.interpreter.intPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSHL(t *testing.T) {
|
func TestSHL(t *testing.T) {
|
||||||
@ -432,6 +436,7 @@ func TestOpMstore(t *testing.T) {
|
|||||||
stack = newstack()
|
stack = newstack()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
)
|
)
|
||||||
|
env.interpreter.intPool = poolOfIntPools.get()
|
||||||
mem.Resize(64)
|
mem.Resize(64)
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||||
@ -445,6 +450,7 @@ func TestOpMstore(t *testing.T) {
|
|||||||
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||||
t.Fatalf("Mstore failed to overwrite previous value")
|
t.Fatalf("Mstore failed to overwrite previous value")
|
||||||
}
|
}
|
||||||
|
poolOfIntPools.put(env.interpreter.intPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOpMstore(bench *testing.B) {
|
func BenchmarkOpMstore(bench *testing.B) {
|
||||||
|
@ -77,7 +77,6 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
|
|||||||
evm: evm,
|
evm: evm,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
|
gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
|
||||||
intPool: newIntPool(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +103,14 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack
|
|||||||
// considered a revert-and-consume-all-gas operation except for
|
// considered a revert-and-consume-all-gas operation except for
|
||||||
// errExecutionReverted which means revert-and-keep-gas-left.
|
// errExecutionReverted which means revert-and-keep-gas-left.
|
||||||
func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||||
|
if in.intPool == nil {
|
||||||
|
in.intPool = poolOfIntPools.get()
|
||||||
|
defer func() {
|
||||||
|
poolOfIntPools.put(in.intPool)
|
||||||
|
in.intPool = nil
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// Increment the call depth which is restricted to 1024
|
// Increment the call depth which is restricted to 1024
|
||||||
in.evm.depth++
|
in.evm.depth++
|
||||||
defer func() { in.evm.depth-- }()
|
defer func() { in.evm.depth-- }()
|
||||||
@ -133,6 +140,9 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
|
|||||||
)
|
)
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
||||||
|
// Reclaim the stack as an int pool when the execution stops
|
||||||
|
defer func() { in.intPool.put(stack.data...) }()
|
||||||
|
|
||||||
if in.cfg.Debug {
|
if in.cfg.Debug {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "math/big"
|
import (
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
var checkVal = big.NewInt(-42)
|
var checkVal = big.NewInt(-42)
|
||||||
|
|
||||||
@ -65,3 +68,39 @@ func (p *intPool) put(is ...*big.Int) {
|
|||||||
p.pool.push(i)
|
p.pool.push(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The intPool pool's default capacity
|
||||||
|
const poolDefaultCap = 25
|
||||||
|
|
||||||
|
// intPoolPool manages a pool of intPools.
|
||||||
|
type intPoolPool struct {
|
||||||
|
pools []*intPool
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var poolOfIntPools = &intPoolPool{
|
||||||
|
pools: make([]*intPool, 0, poolDefaultCap),
|
||||||
|
}
|
||||||
|
|
||||||
|
// get is looking for an available pool to return.
|
||||||
|
func (ipp *intPoolPool) get() *intPool {
|
||||||
|
ipp.lock.Lock()
|
||||||
|
defer ipp.lock.Unlock()
|
||||||
|
|
||||||
|
if len(poolOfIntPools.pools) > 0 {
|
||||||
|
ip := ipp.pools[len(ipp.pools)-1]
|
||||||
|
ipp.pools = ipp.pools[:len(ipp.pools)-1]
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
return newIntPool()
|
||||||
|
}
|
||||||
|
|
||||||
|
// put a pool that has been allocated with get.
|
||||||
|
func (ipp *intPoolPool) put(ip *intPool) {
|
||||||
|
ipp.lock.Lock()
|
||||||
|
defer ipp.lock.Unlock()
|
||||||
|
|
||||||
|
if len(ipp.pools) < cap(ipp.pools) {
|
||||||
|
ipp.pools = append(ipp.pools, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
55
core/vm/intpool_test.go
Normal file
55
core/vm/intpool_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntPoolPoolGet(t *testing.T) {
|
||||||
|
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
||||||
|
|
||||||
|
nip := poolOfIntPools.get()
|
||||||
|
if nip == nil {
|
||||||
|
t.Fatalf("Invalid pool allocation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntPoolPoolPut(t *testing.T) {
|
||||||
|
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
||||||
|
|
||||||
|
nip := poolOfIntPools.get()
|
||||||
|
if len(poolOfIntPools.pools) != 0 {
|
||||||
|
t.Fatalf("Pool got added to list when none should have been")
|
||||||
|
}
|
||||||
|
|
||||||
|
poolOfIntPools.put(nip)
|
||||||
|
if len(poolOfIntPools.pools) == 0 {
|
||||||
|
t.Fatalf("Pool did not get added to list when one should have been")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntPoolPoolReUse(t *testing.T) {
|
||||||
|
poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
|
||||||
|
nip := poolOfIntPools.get()
|
||||||
|
poolOfIntPools.put(nip)
|
||||||
|
poolOfIntPools.get()
|
||||||
|
|
||||||
|
if len(poolOfIntPools.pools) != 0 {
|
||||||
|
t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user