EVM gas for memory tiny optimization (#1578)
* EVM bin benches * Optimizing mem gas cost * Removing overflow_div since it's not used * More benchmarks
This commit is contained in:
parent
c5ed363bba
commit
92fd00f41e
@ -107,6 +107,9 @@ pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Out
|
|||||||
fn overflow_add(self, other: Self) -> (Self, bool);
|
fn overflow_add(self, other: Self) -> (Self, bool);
|
||||||
/// Multiple with overflow
|
/// Multiple with overflow
|
||||||
fn overflow_mul(self, other: Self) -> (Self, bool);
|
fn overflow_mul(self, other: Self) -> (Self, bool);
|
||||||
|
/// Single-step full multiplication and division: `self*other/div`
|
||||||
|
/// Should not overflow on intermediate steps
|
||||||
|
fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CostType for U256 {
|
impl CostType for U256 {
|
||||||
@ -129,6 +132,17 @@ impl CostType for U256 {
|
|||||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||||
Uint::overflowing_mul(self, other)
|
Uint::overflowing_mul(self, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool) {
|
||||||
|
let x = self.full_mul(other);
|
||||||
|
let (U512(parts), o) = Uint::overflowing_div(x, U512::from(div));
|
||||||
|
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
|
||||||
|
|
||||||
|
(
|
||||||
|
U256([parts[0], parts[1], parts[2], parts[3]]),
|
||||||
|
o | overflow
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CostType for usize {
|
impl CostType for usize {
|
||||||
@ -154,6 +168,14 @@ impl CostType for usize {
|
|||||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||||
self.overflowing_mul(other)
|
self.overflowing_mul(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool) {
|
||||||
|
let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
|
||||||
|
let (U128(parts), o1) = c.overflowing_div(U128::from(div));
|
||||||
|
let result = parts[0] as usize;
|
||||||
|
let overflow = o | o1 | (parts[1] > 0) | (parts[0] > result as u64);
|
||||||
|
(result, overflow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evm interface
|
/// Evm interface
|
||||||
@ -164,3 +186,41 @@ pub trait Evm {
|
|||||||
/// to compute the final gas left.
|
/// to compute the final gas left.
|
||||||
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
|
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_calculate_overflow_mul_div_without_overflow() {
|
||||||
|
// given
|
||||||
|
let num = 10_000_000;
|
||||||
|
|
||||||
|
// when
|
||||||
|
let (res1, o1) = U256::from(num).overflow_mul_div(U256::from(num), U256::from(num));
|
||||||
|
let (res2, o2) = num.overflow_mul_div(num, num);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1, U256::from(num));
|
||||||
|
assert!(!o1);
|
||||||
|
assert_eq!(res2, num);
|
||||||
|
assert!(!o2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_calculate_overflow_mul_div_with_overflow() {
|
||||||
|
// given
|
||||||
|
let max = ::std::u64::MAX;
|
||||||
|
let num1 = U256([max, max, max, max]);
|
||||||
|
let num2 = ::std::usize::MAX;
|
||||||
|
|
||||||
|
// when
|
||||||
|
let (res1, o1) = num1.overflow_mul_div(num1, num1 - U256::from(2));
|
||||||
|
let (res2, o2) = num2.overflow_mul_div(num2, num2 - 2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
// (x+2)^2/x = (x^2 + 4x + 4)/x = x + 4 + 4/x ~ (MAX-2) + 4 + 0 = 1
|
||||||
|
assert_eq!(res2, 1);
|
||||||
|
assert!(o2);
|
||||||
|
|
||||||
|
assert_eq!(res1, U256::from(1));
|
||||||
|
assert!(o1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,9 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||||
|
|
||||||
let cost = match instruction {
|
let cost = match instruction {
|
||||||
|
instructions::JUMPDEST => {
|
||||||
|
InstructionCost::Gas(Gas::from(1))
|
||||||
|
},
|
||||||
instructions::SSTORE => {
|
instructions::SSTORE => {
|
||||||
let address = H256::from(stack.peek(0));
|
let address = H256::from(stack.peek(0));
|
||||||
let newval = stack.peek(1);
|
let newval = stack.peek(1);
|
||||||
@ -106,9 +109,6 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
instructions::EXTCODECOPY => {
|
instructions::EXTCODECOPY => {
|
||||||
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||||
},
|
},
|
||||||
instructions::JUMPDEST => {
|
|
||||||
InstructionCost::Gas(Gas::from(1))
|
|
||||||
},
|
|
||||||
instructions::LOG0...instructions::LOG4 => {
|
instructions::LOG0...instructions::LOG4 => {
|
||||||
let no_of_topics = instructions::get_log_topics(instruction);
|
let no_of_topics = instructions::get_log_topics(instruction);
|
||||||
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
||||||
@ -199,14 +199,12 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let s = mem_size >> 5;
|
let s = mem_size >> 5;
|
||||||
// s * memory_gas + s * s / quad_coeff_div
|
// s * memory_gas + s * s / quad_coeff_div
|
||||||
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
|
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
|
||||||
// We need to go to U512 to calculate s*s/quad_coeff_div
|
|
||||||
let b = U512::from(s.as_u256()) * U512::from(s.as_u256()) / U512::from(schedule.quad_coeff_div);
|
// Calculate s*s/quad_coeff_div
|
||||||
if b > U512::from(!U256::zero()) {
|
let b = overflowing!(s.overflow_mul_div(s, Gas::from(schedule.quad_coeff_div)));
|
||||||
Err(evm::Error::OutOfGas)
|
Ok(overflowing!(a.overflow_add(b)))
|
||||||
} else {
|
|
||||||
Ok(overflowing!(a.overflow_add(try!(Gas::from_u256(U256::from(b))))))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_mem_size = Gas::from(current_mem_size);
|
let current_mem_size = Gas::from(current_mem_size);
|
||||||
let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
|
let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
|
||||||
|
|
||||||
|
@ -4,6 +4,14 @@ description = "Parity's EVM implementation"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Ethcore <admin@ethcore.io>"]
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "evm"
|
||||||
|
path = "./src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "evm"
|
||||||
|
path = "./src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
docopt = { version = "0.6" }
|
docopt = { version = "0.6" }
|
||||||
|
24
evmbin/bench.sh
Executable file
24
evmbin/bench.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -x
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# LOOP TEST
|
||||||
|
CODE1=606060405260005b620f42408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200
|
||||||
|
ethvm --code $CODE1
|
||||||
|
echo "^^^^ ethvm"
|
||||||
|
./target/release/evm stats --code $CODE1 --gas 4402000
|
||||||
|
echo "^^^^ usize"
|
||||||
|
./target/release/evm stats --code $CODE1
|
||||||
|
echo "^^^^ U256"
|
||||||
|
|
||||||
|
# RNG TEST
|
||||||
|
CODE2=6060604052600360056007600b60005b620f4240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200
|
||||||
|
ethvm --code $CODE2
|
||||||
|
echo "^^^^ ethvm"
|
||||||
|
./target/release/evm stats --code $CODE2 --gas 143020115
|
||||||
|
echo "^^^^ usize"
|
||||||
|
./target/release/evm stats --code $CODE2
|
||||||
|
echo "^^^^ U256"
|
85
evmbin/benches/mod.rs
Normal file
85
evmbin/benches/mod.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity 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 General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! benchmarking for EVM
|
||||||
|
//! should be started with:
|
||||||
|
//! ```bash
|
||||||
|
//! multirust run nightly cargo bench
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
extern crate ethcore;
|
||||||
|
extern crate evm;
|
||||||
|
extern crate ethcore_util;
|
||||||
|
extern crate rustc_serialize;
|
||||||
|
|
||||||
|
use self::test::{Bencher, black_box};
|
||||||
|
|
||||||
|
use evm::run_vm;
|
||||||
|
use ethcore::action_params::ActionParams;
|
||||||
|
use ethcore_util::{U256, Uint};
|
||||||
|
use rustc_serialize::hex::FromHex;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn simple_loop_usize(b: &mut Bencher) {
|
||||||
|
simple_loop(U256::from(::std::usize::MAX), b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn simple_loop_u256(b: &mut Bencher) {
|
||||||
|
simple_loop(!U256::zero(), b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simple_loop(gas: U256, b: &mut Bencher) {
|
||||||
|
let code = black_box(
|
||||||
|
"606060405260005b620042408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200".from_hex().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = gas;
|
||||||
|
params.code = Some(code.clone());
|
||||||
|
|
||||||
|
run_vm(params)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn rng_usize(b: &mut Bencher) {
|
||||||
|
rng(U256::from(::std::usize::MAX), b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn rng_u256(b: &mut Bencher) {
|
||||||
|
rng(!U256::zero(), b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rng(gas: U256, b: &mut Bencher) {
|
||||||
|
let code = black_box(
|
||||||
|
"6060604052600360056007600b60005b62004240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200".from_hex().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = gas;
|
||||||
|
params.code = Some(code.clone());
|
||||||
|
|
||||||
|
run_vm(params)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
|||||||
//! Parity EVM interpreter binary.
|
//! Parity EVM interpreter binary.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
#![allow(dead_code)]
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
@ -25,7 +26,7 @@ extern crate ethcore_util as util;
|
|||||||
|
|
||||||
mod ext;
|
mod ext;
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::{Instant, Duration};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use util::{U256, FromHex, Uint, Bytes};
|
use util::{U256, FromHex, Uint, Bytes};
|
||||||
@ -58,6 +59,15 @@ fn main() {
|
|||||||
params.code = Some(args.code());
|
params.code = Some(args.code());
|
||||||
params.data = args.data();
|
params.data = args.data();
|
||||||
|
|
||||||
|
let result = run_vm(params);
|
||||||
|
println!("Gas used: {:?}", result.gas_used);
|
||||||
|
println!("Output: {:?}", result.output);
|
||||||
|
println!("Time: {}.{:.9}s", result.time.as_secs(), result.time.subsec_nanos());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute VM with given `ActionParams`
|
||||||
|
pub fn run_vm(params: ActionParams) -> ExecutionResults {
|
||||||
|
let initial_gas = params.gas;
|
||||||
let factory = Factory::new(VMType::Interpreter);
|
let factory = Factory::new(VMType::Interpreter);
|
||||||
let mut vm = factory.create(params.gas);
|
let mut vm = factory.create(params.gas);
|
||||||
let mut ext = ext::FakeExt::default();
|
let mut ext = ext::FakeExt::default();
|
||||||
@ -66,9 +76,21 @@ fn main() {
|
|||||||
let gas_left = vm.exec(params, &mut ext).finalize(ext).expect("OK");
|
let gas_left = vm.exec(params, &mut ext).finalize(ext).expect("OK");
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
|
|
||||||
println!("Gas used: {:?}", args.gas() - gas_left);
|
ExecutionResults {
|
||||||
println!("Output: {:?}", "");
|
gas_used: initial_gas - gas_left,
|
||||||
println!("Time: {}.{:.9}s", duration.as_secs(), duration.subsec_nanos());
|
output: Vec::new(),
|
||||||
|
time: duration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// VM execution results
|
||||||
|
pub struct ExecutionResults {
|
||||||
|
/// Used gas
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Output as bytes
|
||||||
|
pub output: Vec<u8>,
|
||||||
|
/// Time Taken
|
||||||
|
pub time: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, RustcDecodable)]
|
#[derive(Debug, RustcDecodable)]
|
||||||
|
Loading…
Reference in New Issue
Block a user