aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillaume Ballet <gballet@gmail.com>2018-07-03 18:06:42 +0800
committerPéter Szilágyi <peterke@gmail.com>2018-07-03 18:06:42 +0800
commit4e5d1f1c39159de42511770bd390ad583ebd57a5 (patch)
treed1ce3fb28c78dc6bdc73026eacfee92fa71ce447
parentd57e85ecc91608a9095479365308a285f05c755d (diff)
downloaddexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.tar
dexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.tar.gz
dexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.tar.bz2
dexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.tar.lz
dexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.tar.xz
dexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.tar.zst
dexon-4e5d1f1c39159de42511770bd390ad583ebd57a5.zip
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
-rw-r--r--core/vm/instructions_test.go6
-rw-r--r--core/vm/interpreter.go12
-rw-r--r--core/vm/intpool.go41
-rw-r--r--core/vm/intpool_test.go55
4 files changed, 112 insertions, 2 deletions
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index f51e6363f..1643da967 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -36,6 +36,7 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
stack = newstack()
pc = uint64(0)
)
+ env.interpreter.intPool = poolOfIntPools.get()
for i, test := range tests {
x := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
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) {
@@ -71,6 +73,7 @@ func TestByteOp(t *testing.T) {
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
)
+ env.interpreter.intPool = poolOfIntPools.get()
tests := []struct {
v string
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)
}
}
+ poolOfIntPools.put(env.interpreter.intPool)
}
func TestSHL(t *testing.T) {
@@ -432,6 +436,7 @@ func TestOpMstore(t *testing.T) {
stack = newstack()
mem = NewMemory()
)
+ env.interpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
@@ -445,6 +450,7 @@ func TestOpMstore(t *testing.T) {
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
+ poolOfIntPools.put(env.interpreter.intPool)
}
func BenchmarkOpMstore(bench *testing.B) {
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 7090e0261..0d6038cbb 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -77,7 +77,6 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
evm: evm,
cfg: cfg,
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
// errExecutionReverted which means revert-and-keep-gas-left.
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
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
+ // Reclaim the stack as an int pool when the execution stops
+ defer func() { in.intPool.put(stack.data...) }()
+
if in.cfg.Debug {
defer func() {
if err != nil {
diff --git a/core/vm/intpool.go b/core/vm/intpool.go
index 5dbda18ee..917a78d56 100644
--- a/core/vm/intpool.go
+++ b/core/vm/intpool.go
@@ -16,7 +16,10 @@
package vm
-import "math/big"
+import (
+ "math/big"
+ "sync"
+)
var checkVal = big.NewInt(-42)
@@ -65,3 +68,39 @@ func (p *intPool) put(is ...*big.Int) {
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)
+ }
+}
diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go
new file mode 100644
index 000000000..6c0d00f3c
--- /dev/null
+++ b/core/vm/intpool_test.go
@@ -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)
+ }
+}