From f80d102fe40fc5b8d57182c44db5e093ac7d65ed Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Tue, 27 Sep 2016 14:11:22 +0200 Subject: [PATCH] wip --- common/big_test.go | 35 ++++++++++++++++++++++++++++++++++ core/state/state.go | 21 ++++++++++++++++---- core/vm/instructions.go | 2 +- core/vm/program_optimiser.go | 19 ------------------ core/vm/stack.go | 20 ++++++++++++------- core/vm/vm.go | 37 ++++++++++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 31 deletions(-) diff --git a/common/big_test.go b/common/big_test.go index 1eb0c0c1fd..4547ddbd3a 100644 --- a/common/big_test.go +++ b/common/big_test.go @@ -18,6 +18,7 @@ package common import ( "bytes" + "math/big" "testing" ) @@ -87,3 +88,37 @@ func TestBigCopy(t *testing.T) { t.Error("Got", zbytes) } } + +var benchSize = 1000 + +func BenchmarkBitLen(b *testing.B) { + tests := make([]*big.Int, 0, benchSize) + for i := 0; i < benchSize; i++ { + tests = append(tests, big.NewInt(int64(i))) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for j := 0; j < benchSize; j++ { + _ = tests[j].BitLen() + } + } +} + +var bigzero = new(big.Int) + +func BenchmarkCmp(b *testing.B) { + tests := make([]*big.Int, 0, benchSize) + for i := 0; i < benchSize; i++ { + tests = append(tests, big.NewInt(int64(i))) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for j := 0; j < benchSize; j++ { + _ = tests[j].Cmp(bigzero) + } + } +} diff --git a/core/state/state.go b/core/state/state.go index d238813687..db8ada77e3 100644 --- a/core/state/state.go +++ b/core/state/state.go @@ -85,6 +85,7 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool) stateObject := s.StateObjects[address] if stateObject == nil && s.parent != nil { stateObject, _ = s.parent.Read(address) + s.StateObjects[address] = stateObject } else if stateObject != nil { inCache = true } @@ -107,6 +108,7 @@ func (s *State) Read(address common.Address) (object *StateObject, inCache bool) glog.Errorf("can't decode object at %x: %v", address[:], err) return nil, false } + s.StateObjects[address] = stateObject return stateObject, false } @@ -126,9 +128,9 @@ func (s *State) loadStateObject(stateObject *StateObject) { } func (s *State) GetOrNewStateObject(address common.Address) *StateObject { - stateObject, inCache := s.Read(address) + stateObject, _ := s.Read(address) if stateObject != nil { - if !inCache || !s.localStateObjects[address] { + if !s.localStateObjects[address] { stateObject = stateObject.Copy() s.StateObjects[address] = stateObject s.localStateObjects[address] = true @@ -161,9 +163,10 @@ func (s *State) GetOrNewStateObject(address common.Address) *StateObject { */ func (s *State) GetStateObject(address common.Address) *StateObject { - account, inCache := s.Read(address) + account, _ := s.Read(address) if account != nil { - if !inCache { + if s.StateObjects[address] == nil { + //if !inCache { s.StateObjects[address] = account } return account @@ -189,7 +192,13 @@ func (s *State) CreateStateObject(address common.Address) *StateObject { return stateObject } +var bigzero = new(big.Int) + func (s *State) SubBalance(address common.Address, amount *big.Int) { + if amount.Cmp(bigzero) == 0 { + return + } + stateObject := s.GetOrNewStateObject(address) if stateObject != nil { stateObject.SubBalance(amount) @@ -197,6 +206,10 @@ func (s *State) SubBalance(address common.Address, amount *big.Int) { } func (s *State) AddBalance(address common.Address, amount *big.Int) { + if amount.Cmp(bigzero) == 0 { + return + } + stateObject := s.GetOrNewStateObject(address) if stateObject != nil { stateObject.AddBalance(amount) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0a3d3caf69..a2792dadd4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -142,7 +142,7 @@ func opMul(instr instruction, pc *uint64, env *Environment, contract *Contract, func opDiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) { x, y := stack.pop(), stack.pop() - if y.Cmp(common.Big0) != 0 { + if y.BitLen() != 0 { stack.push(U256(x.Div(x, y))) } else { stack.push(new(big.Int)) diff --git a/core/vm/program_optimiser.go b/core/vm/program_optimiser.go index 8322c83471..79c91df3e6 100644 --- a/core/vm/program_optimiser.go +++ b/core/vm/program_optimiser.go @@ -20,7 +20,6 @@ import ( "math/big" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) @@ -86,8 +85,6 @@ func OptimiseProgram(program *Program) { statsJump++ load = nil - case instr.op == EXTCODESIZE: - program.instructions[i] = codesize{} default: // create a new N pushes segment if len(load) > 1 { @@ -100,22 +97,6 @@ func OptimiseProgram(program *Program) { } } -type codesize struct { - size *big.Int -} - -func (instr codesize) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - if instr.size == nil { - addr := common.BigToAddress(stack.pop()) - instr.size = big.NewInt(int64(len(env.Db().GetCode(addr)))) - } - stack.push(instr.size) - return nil, nil -} - -func (codesize) Op() OpCode { return EXTCODESIZE } -func (codesize) halts() bool { return false } - // makePushSeg creates a new push segment from N amount of push instructions func makePushSeg(instrs []instruction) (pushSeg, int) { var ( diff --git a/core/vm/stack.go b/core/vm/stack.go index f6c1f76e45..2c9a31d9f4 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -25,7 +25,8 @@ import ( // expected to be changed and modified. stack does not take care of adding newly // initialised objects. type Stack struct { - data []*big.Int + data []*big.Int + count int } func newstack() *Stack { @@ -40,16 +41,21 @@ func (st *Stack) push(d *big.Int) { // NOTE push limit (1024) is checked in baseCheck //stackItem := new(big.Int).Set(d) //st.data = append(st.data, stackItem) - st.data = append(st.data, d) + //st.data = append(st.data, d) + st.data = append(st.data[:st.count], d) + st.count++ } func (st *Stack) pushN(ds ...*big.Int) { - st.data = append(st.data, ds...) + st.data = append(st.data[:st.count], ds...) + st.count += len(ds) } -func (st *Stack) pop() (ret *big.Int) { - ret = st.data[len(st.data)-1] - st.data = st.data[:len(st.data)-1] - return +func (st *Stack) pop() *big.Int { + //ret = st.data[len(st.data)-1] + //st.data = st.data[:len(st.data)-1] + + st.count-- + return st.data[st.count] } func (st *Stack) len() int { diff --git a/core/vm/vm.go b/core/vm/vm.go index 658a77bee5..95c39e5ae1 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -18,6 +18,7 @@ package vm import ( "fmt" + "sort" "time" "github.com/ethereum/go-ethereum/crypto" @@ -103,6 +104,18 @@ func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) { return nil, fmt.Errorf("Unexpected return using program %x", codehash) } +type stats struct { + op OpCode + dura time.Duration + callCount uint64 +} + +type statsList []stats + +func (sl statsList) Len() int { return len(sl) } +func (sl statsList) Less(i, j int) bool { return sl[i].dura < sl[j].dura } +func (sl statsList) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] } + func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) ([]byte, error) { contract.Input = input @@ -112,7 +125,23 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) ( mem = NewMemory() stack = newstack() env = evm.env + stats = make(map[OpCode]stats) ) + defer func() { + fmt.Println("stats for program") + i := 0 + sl := make(statsList, len(stats)) + for op, stat := range stats { + stat.op = op + sl[i] = stat + i++ + } + + sort.Sort(sort.Reverse(sl)) + for _, stat := range sl { + fmt.Printf("%v: call count: %d duration: %v\n", stat.op, stat.callCount, stat.dura) + } + }() if glog.V(logger.Debug) { glog.Infof("running JIT program %x\n", program.Id[:4]) @@ -127,11 +156,19 @@ func (evm *EVM) runProgram(program *Program, contract *Contract, input []byte) ( instrCount++ instr := program.instructions[pc] + + tstart := time.Now() + stat := stats[instr.Op()] + + stat.callCount++ if instr.Op() == DELEGATECALL && !homestead { return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op()) } ret, err := instr.do(program, &pc, env, contract, mem, stack) + stat.dura += time.Since(tstart) + + stats[instr.Op()] = stat if err != nil { //gas := new(big.Int).SetUint64(contract.gas64) //evm.cfg.Tracer.CaptureState(evm.env, pc, instr.Op(), gas, cost, mem, stack, contract, evm.env.Depth(), err)