mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-06-13 10:31:10 +00:00
Add new gas metering method: mutable global + local gas function (#34)
* fix misprints in doc comments * added global gas tracker variable and local gas fn * all exported functions of the module to accept a new param and to set the gas_left global to its value at their very start * make module support both gas metering methods * tests fixed for the old metering method * better naming * MutableGlobal metering method implemented, tests for the old method pass * gas_metering::tests updated and pass * all tests udpdated and pass * emacs backup files to .gitignore * docs updated * clippy fix * iff = if and only if * more clippy * docs misprints fixes * refactored to have Backend trait and two implementations in separate sub-modules * docs updated * fixed old benches (updating them is coming next) * added bench for an instrumented wasm-coremark * updated benches: added them for both gas_metering instrumentations * benches contest first ver * added debug prints to the bench * refactored to better fit frontend-backend pattern * docs update * updated benches * design updated on feedback * re-structured sub-modules re-structured sub-modules & updated docs * docs improved * addressed latest feedback comments * re-writed the local gas function * coremark benches show ~20% performance improvement * fix ci: test + clippy * save before re-factoring prepare_in_wasm() * bare_call_16 shows 16% worse perf * + fibonacci recursive bench * refactored benchmarks * + factorial recursive bench * benches on wasmi fixtures show no perf improvement, coremark runs ~20% faster being instrumented with mutable_global gas metering * charge gas for local gas func isntructions execution * replaced benchmark which requires multi_value feature * save: optimized gas func a bit (benches work, fixture tests fail) * 1033% overhead on many_blocks.wasm when mut_global gas_metering together with stack_height * size overhead test for both gas metering methods + stack limiter * added more benches * improved print_size_overhead test * test for comparing size overheads of two gas_metering injectors * before optimization: benches + size overhead * optimization try-1: inline part of gas func instructions: +benches +size overheads * optimization try-2: inline hot path of gas fn: +benches +size overheads * opt try-3: count for gas fn cost on the caller side: +benches +size overhead * revert to initial version but with static gas fn cost on the caller side: +benches +sizes * tests fixed * use newest wasmi 0.20: +benches +docs updated * use if-else block instead of Return: +benches * fix tests * clippy fix * addressed review comments * Update changelog Co-authored-by: Alexander Theißen <alex.theissen@me.com>
This commit is contained in:
+64
-29
@@ -1,10 +1,9 @@
|
||||
use parity_wasm::elements::Module;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use wasm_instrument::{self as instrument, parity_wasm::elements};
|
||||
use wasm_instrument::{self as instrument, gas_metering, parity_wasm::elements};
|
||||
use wasmparser::validate;
|
||||
|
||||
fn slurp<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
||||
@@ -20,18 +19,23 @@ fn dump<P: AsRef<Path>>(path: P, buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_diff_test<F: FnOnce(&[u8]) -> Vec<u8>>(test_dir: &str, name: &str, test: F) {
|
||||
fn run_diff_test<F: FnOnce(&[u8]) -> Vec<u8>>(
|
||||
test_dir: &str,
|
||||
in_name: &str,
|
||||
out_name: &str,
|
||||
test: F,
|
||||
) {
|
||||
let mut fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
fixture_path.push("tests");
|
||||
fixture_path.push("fixtures");
|
||||
fixture_path.push(test_dir);
|
||||
fixture_path.push(name);
|
||||
fixture_path.push(in_name);
|
||||
|
||||
let mut expected_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
expected_path.push("tests");
|
||||
expected_path.push("expectations");
|
||||
expected_path.push(test_dir);
|
||||
expected_path.push(name);
|
||||
expected_path.push(out_name);
|
||||
|
||||
let fixture_wasm = wat::parse_file(&fixture_path).expect("Failed to read fixture");
|
||||
validate(&fixture_wasm).expect("Fixture is invalid");
|
||||
@@ -48,7 +52,7 @@ fn run_diff_test<F: FnOnce(&[u8]) -> Vec<u8>>(test_dir: &str, name: &str, test:
|
||||
if actual_wat != expected_wat {
|
||||
println!("difference!");
|
||||
println!("--- {}", expected_path.display());
|
||||
println!("+++ {} test {}", test_dir, name);
|
||||
println!("+++ {} test {}", test_dir, out_name);
|
||||
for diff in diff::lines(expected_wat, &actual_wat) {
|
||||
match diff {
|
||||
diff::Result::Left(l) => println!("-{}", l),
|
||||
@@ -72,13 +76,18 @@ mod stack_height {
|
||||
( $name:ident ) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
run_diff_test("stack-height", concat!(stringify!($name), ".wat"), |input| {
|
||||
let module =
|
||||
elements::deserialize_buffer(input).expect("Failed to deserialize");
|
||||
let instrumented = instrument::inject_stack_limiter(module, 1024)
|
||||
.expect("Failed to instrument with stack counter");
|
||||
elements::serialize(instrumented).expect("Failed to serialize")
|
||||
});
|
||||
run_diff_test(
|
||||
"stack-height",
|
||||
concat!(stringify!($name), ".wat"),
|
||||
concat!(stringify!($name), ".wat"),
|
||||
|input| {
|
||||
let module =
|
||||
elements::deserialize_buffer(input).expect("Failed to deserialize");
|
||||
let instrumented = instrument::inject_stack_limiter(module, 1024)
|
||||
.expect("Failed to instrument with stack counter");
|
||||
elements::serialize(instrumented).expect("Failed to serialize")
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -96,27 +105,53 @@ mod gas {
|
||||
use super::*;
|
||||
|
||||
macro_rules! def_gas_test {
|
||||
( $name:ident ) => {
|
||||
( ($input:ident, $name1:ident, $name2:ident) ) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
run_diff_test("gas", concat!(stringify!($name), ".wat"), |input| {
|
||||
let rules = instrument::gas_metering::ConstantCostRules::default();
|
||||
fn $name1() {
|
||||
run_diff_test(
|
||||
"gas",
|
||||
concat!(stringify!($input), ".wat"),
|
||||
concat!(stringify!($name1), ".wat"),
|
||||
|input| {
|
||||
let rules = gas_metering::ConstantCostRules::default();
|
||||
|
||||
let module: Module =
|
||||
elements::deserialize_buffer(input).expect("Failed to deserialize");
|
||||
let module = module.parse_names().expect("Failed to parse names");
|
||||
let module: elements::Module =
|
||||
elements::deserialize_buffer(input).expect("Failed to deserialize");
|
||||
let module = module.parse_names().expect("Failed to parse names");
|
||||
let backend = gas_metering::host_function::Injector::new("env", "gas");
|
||||
|
||||
let instrumented = instrument::gas_metering::inject(module, &rules, "env")
|
||||
.expect("Failed to instrument with gas metering");
|
||||
elements::serialize(instrumented).expect("Failed to serialize")
|
||||
});
|
||||
let instrumented = gas_metering::inject(module, backend, &rules)
|
||||
.expect("Failed to instrument with gas metering");
|
||||
elements::serialize(instrumented).expect("Failed to serialize")
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn $name2() {
|
||||
run_diff_test(
|
||||
"gas",
|
||||
concat!(stringify!($input), ".wat"),
|
||||
concat!(stringify!($name2), ".wat"),
|
||||
|input| {
|
||||
let rules = gas_metering::ConstantCostRules::default();
|
||||
|
||||
let module: elements::Module =
|
||||
elements::deserialize_buffer(input).expect("Failed to deserialize");
|
||||
let module = module.parse_names().expect("Failed to parse names");
|
||||
let backend = gas_metering::mutable_global::Injector::new("gas_left");
|
||||
let instrumented = gas_metering::inject(module, backend, &rules)
|
||||
.expect("Failed to instrument with gas metering");
|
||||
elements::serialize(instrumented).expect("Failed to serialize")
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
def_gas_test!(ifs);
|
||||
def_gas_test!(simple);
|
||||
def_gas_test!(start);
|
||||
def_gas_test!(call);
|
||||
def_gas_test!(branch);
|
||||
def_gas_test!((ifs, ifs_host_fn, ifs_mut_global));
|
||||
def_gas_test!((simple, simple_host_fn, simple_mut_global));
|
||||
def_gas_test!((start, start_host_fn, start_mut_global));
|
||||
def_gas_test!((call, call_host_fn, call_mut_global));
|
||||
def_gas_test!((branch, branch_host_fn, branch_mut_global));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
(module
|
||||
(type (;0;) (func (result i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(func $fibonacci_with_break (;0;) (type 0) (result i32)
|
||||
(local $x i32) (local $y i32)
|
||||
i64.const 24
|
||||
call 1
|
||||
block ;; label = @1
|
||||
i32.const 0
|
||||
local.set $x
|
||||
i32.const 1
|
||||
local.set $y
|
||||
local.get $x
|
||||
local.get $y
|
||||
local.tee $x
|
||||
i32.add
|
||||
local.set $y
|
||||
i32.const 1
|
||||
br_if 0 (;@1;)
|
||||
i64.const 16
|
||||
call 1
|
||||
local.get $x
|
||||
local.get $y
|
||||
local.tee $x
|
||||
i32.add
|
||||
local.set $y
|
||||
end
|
||||
local.get $y
|
||||
)
|
||||
(func (;1;) (type 1) (param i64)
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.ge_u
|
||||
if ;; label = @1
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.sub
|
||||
global.set 0
|
||||
else
|
||||
i64.const -1
|
||||
global.set 0
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(global (;0;) (mut i64) i64.const 0)
|
||||
(export "gas_left" (global 0))
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32 i32) (result i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(func $add_locals (;0;) (type 0) (param $x i32) (param $y i32) (result i32)
|
||||
(local $t i32)
|
||||
i64.const 16
|
||||
call 2
|
||||
local.get $x
|
||||
local.get $y
|
||||
call $add
|
||||
local.set $t
|
||||
local.get $t
|
||||
)
|
||||
(func $add (;1;) (type 0) (param $x i32) (param $y i32) (result i32)
|
||||
i64.const 14
|
||||
call 2
|
||||
local.get $x
|
||||
local.get $y
|
||||
i32.add
|
||||
)
|
||||
(func (;2;) (type 1) (param i64)
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.ge_u
|
||||
if ;; label = @1
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.sub
|
||||
global.set 0
|
||||
else
|
||||
i64.const -1
|
||||
global.set 0
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(global (;0;) (mut i64) i64.const 0)
|
||||
(export "gas_left" (global 0))
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32) (result i32)))
|
||||
(type (;1;) (func (param i64)))
|
||||
(func (;0;) (type 0) (param $x i32) (result i32)
|
||||
i64.const 13
|
||||
call 1
|
||||
i32.const 1
|
||||
if (result i32) ;; label = @1
|
||||
i64.const 14
|
||||
call 1
|
||||
local.get $x
|
||||
i32.const 1
|
||||
i32.add
|
||||
else
|
||||
i64.const 13
|
||||
call 1
|
||||
local.get $x
|
||||
i32.popcnt
|
||||
end
|
||||
)
|
||||
(func (;1;) (type 1) (param i64)
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.ge_u
|
||||
if ;; label = @1
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.sub
|
||||
global.set 0
|
||||
else
|
||||
i64.const -1
|
||||
global.set 0
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(global (;0;) (mut i64) i64.const 0)
|
||||
(export "gas_left" (global 0))
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(type (;1;) (func (param i64)))
|
||||
(func (;0;) (type 0)
|
||||
i64.const 13
|
||||
call 2
|
||||
i32.const 1
|
||||
if ;; label = @1
|
||||
i64.const 12
|
||||
call 2
|
||||
loop ;; label = @2
|
||||
i64.const 13
|
||||
call 2
|
||||
i32.const 123
|
||||
drop
|
||||
end
|
||||
end
|
||||
)
|
||||
(func (;1;) (type 0)
|
||||
i64.const 12
|
||||
call 2
|
||||
block ;; label = @1
|
||||
end
|
||||
)
|
||||
(func (;2;) (type 1) (param i64)
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.ge_u
|
||||
if ;; label = @1
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.sub
|
||||
global.set 0
|
||||
else
|
||||
i64.const -1
|
||||
global.set 0
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(global (;0;) (mut i64) i64.const 0)
|
||||
(export "simple" (func 0))
|
||||
(export "gas_left" (global 0))
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32 i32)))
|
||||
(type (;1;) (func))
|
||||
(type (;2;) (func (param i64)))
|
||||
(import "env" "ext_return" (func $ext_return (;0;) (type 0)))
|
||||
(import "env" "memory" (memory (;0;) 1 1))
|
||||
(func $start (;1;) (type 1)
|
||||
i64.const 15
|
||||
call 3
|
||||
i32.const 8
|
||||
i32.const 4
|
||||
call $ext_return
|
||||
unreachable
|
||||
)
|
||||
(func (;2;) (type 1))
|
||||
(func (;3;) (type 2) (param i64)
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.ge_u
|
||||
if ;; label = @1
|
||||
global.get 0
|
||||
local.get 0
|
||||
i64.sub
|
||||
global.set 0
|
||||
else
|
||||
i64.const -1
|
||||
global.set 0
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(global (;0;) (mut i64) i64.const 0)
|
||||
(export "call" (func 2))
|
||||
(export "gas_left" (global 0))
|
||||
(start $start)
|
||||
(data (;0;) (i32.const 8) "\01\02\03\04")
|
||||
)
|
||||
+146
-48
@@ -1,9 +1,10 @@
|
||||
use std::{
|
||||
fs::{read, read_dir},
|
||||
fs::{read, read_dir, ReadDir},
|
||||
path::PathBuf,
|
||||
};
|
||||
use wasm_instrument::{
|
||||
gas_metering, inject_stack_limiter,
|
||||
gas_metering::{self, host_function, mutable_global, ConstantCostRules},
|
||||
inject_stack_limiter,
|
||||
parity_wasm::{deserialize_buffer, elements::Module, serialize},
|
||||
};
|
||||
|
||||
@@ -14,60 +15,157 @@ fn fixture_dir() -> PathBuf {
|
||||
path
|
||||
}
|
||||
|
||||
/// Print the overhead of applying gas metering, stack height limiting or both.
|
||||
///
|
||||
/// Use `cargo test print_overhead -- --nocapture`.
|
||||
#[test]
|
||||
fn print_size_overhead() {
|
||||
let mut results: Vec<_> = read_dir(fixture_dir())
|
||||
.unwrap()
|
||||
use gas_metering::Backend;
|
||||
fn gas_metered_mod_len<B: Backend>(orig_module: Module, backend: B) -> (Module, usize) {
|
||||
let module = gas_metering::inject(orig_module, backend, &ConstantCostRules::default()).unwrap();
|
||||
let bytes = serialize(module.clone()).unwrap();
|
||||
let len = bytes.len();
|
||||
(module, len)
|
||||
}
|
||||
|
||||
fn stack_limited_mod_len(module: Module) -> (Module, usize) {
|
||||
let module = inject_stack_limiter(module, 128).unwrap();
|
||||
let bytes = serialize(module.clone()).unwrap();
|
||||
let len = bytes.len();
|
||||
(module, len)
|
||||
}
|
||||
|
||||
struct InstrumentedWasmResults {
|
||||
filename: String,
|
||||
original_module_len: usize,
|
||||
stack_limited_len: usize,
|
||||
gas_metered_host_fn_len: usize,
|
||||
gas_metered_mut_glob_len: usize,
|
||||
gas_metered_host_fn_then_stack_limited_len: usize,
|
||||
gas_metered_mut_glob_then_stack_limited_len: usize,
|
||||
}
|
||||
|
||||
fn size_overheads_all(files: ReadDir) -> Vec<InstrumentedWasmResults> {
|
||||
files
|
||||
.map(|entry| {
|
||||
let entry = entry.unwrap();
|
||||
let (orig_len, orig_module) = {
|
||||
let bytes = read(&entry.path()).unwrap();
|
||||
let filename = entry.file_name().into_string().unwrap();
|
||||
|
||||
let (original_module_len, orig_module) = {
|
||||
let bytes = match entry.path().extension().unwrap().to_str() {
|
||||
Some("wasm") => read(&entry.path()).unwrap(),
|
||||
Some("wat") =>
|
||||
wat::parse_bytes(&read(&entry.path()).unwrap()).unwrap().into_owned(),
|
||||
_ => panic!("expected fixture_dir containing .wasm or .wat files only"),
|
||||
};
|
||||
|
||||
let len = bytes.len();
|
||||
let module: Module = deserialize_buffer(&bytes).unwrap();
|
||||
(len, module)
|
||||
};
|
||||
let (gas_metering_len, gas_module) = {
|
||||
let module = gas_metering::inject(
|
||||
orig_module.clone(),
|
||||
&gas_metering::ConstantCostRules::default(),
|
||||
"env",
|
||||
)
|
||||
.unwrap();
|
||||
let bytes = serialize(module.clone()).unwrap();
|
||||
let len = bytes.len();
|
||||
(len, module)
|
||||
};
|
||||
let stack_height_len = {
|
||||
let module = inject_stack_limiter(orig_module, 128).unwrap();
|
||||
let bytes = serialize(module).unwrap();
|
||||
bytes.len()
|
||||
};
|
||||
let both_len = {
|
||||
let module = inject_stack_limiter(gas_module, 128).unwrap();
|
||||
let bytes = serialize(module).unwrap();
|
||||
bytes.len()
|
||||
};
|
||||
|
||||
let overhead = both_len * 100 / orig_len;
|
||||
let (gm_host_fn_module, gas_metered_host_fn_len) = gas_metered_mod_len(
|
||||
orig_module.clone(),
|
||||
host_function::Injector::new("env", "gas"),
|
||||
);
|
||||
|
||||
(
|
||||
overhead,
|
||||
format!(
|
||||
"{:30}: orig = {:4} kb, gas_metering = {} %, stack_limiter = {} %, both = {} %",
|
||||
entry.file_name().to_str().unwrap(),
|
||||
orig_len / 1024,
|
||||
gas_metering_len * 100 / orig_len,
|
||||
stack_height_len * 100 / orig_len,
|
||||
overhead,
|
||||
),
|
||||
)
|
||||
let (gm_mut_global_module, gas_metered_mut_glob_len) =
|
||||
gas_metered_mod_len(orig_module.clone(), mutable_global::Injector::new("gas_left"));
|
||||
|
||||
let stack_limited_len = stack_limited_mod_len(orig_module).1;
|
||||
|
||||
let (_gm_hf_sl_mod, gas_metered_host_fn_then_stack_limited_len) =
|
||||
stack_limited_mod_len(gm_host_fn_module);
|
||||
|
||||
let (_gm_mg_sl_module, gas_metered_mut_glob_then_stack_limited_len) =
|
||||
stack_limited_mod_len(gm_mut_global_module);
|
||||
|
||||
InstrumentedWasmResults {
|
||||
filename,
|
||||
original_module_len,
|
||||
stack_limited_len,
|
||||
gas_metered_host_fn_len,
|
||||
gas_metered_mut_glob_len,
|
||||
gas_metered_host_fn_then_stack_limited_len,
|
||||
gas_metered_mut_glob_then_stack_limited_len,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
results.sort_unstable_by(|a, b| b.0.cmp(&a.0));
|
||||
for entry in results {
|
||||
println!("{}", entry.1);
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn calc_size_overheads() -> Vec<InstrumentedWasmResults> {
|
||||
let mut wasm_path = fixture_dir();
|
||||
wasm_path.push("wasm");
|
||||
|
||||
let mut wat_path = fixture_dir();
|
||||
wat_path.push("wat");
|
||||
|
||||
let mut results = size_overheads_all(read_dir(wasm_path).unwrap());
|
||||
let results_wat = size_overheads_all(read_dir(wat_path).unwrap());
|
||||
|
||||
results.extend(results_wat);
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
/// Print the overhead of applying gas metering, stack
|
||||
/// height limiting or both.
|
||||
///
|
||||
/// Use `cargo test print_size_overhead -- --nocapture`.
|
||||
#[test]
|
||||
fn print_size_overhead() {
|
||||
let mut results = calc_size_overheads();
|
||||
results.sort_unstable_by(|a, b| {
|
||||
b.gas_metered_mut_glob_then_stack_limited_len
|
||||
.cmp(&a.gas_metered_mut_glob_then_stack_limited_len)
|
||||
});
|
||||
|
||||
for r in results {
|
||||
let filename = r.filename;
|
||||
let original_size = r.original_module_len / 1024;
|
||||
let stack_limit = r.stack_limited_len * 100 / r.original_module_len;
|
||||
let host_fn = r.gas_metered_host_fn_len * 100 / r.original_module_len;
|
||||
let mut_glob = r.gas_metered_mut_glob_len * 100 / r.original_module_len;
|
||||
let host_fn_sl = r.gas_metered_host_fn_then_stack_limited_len * 100 / r.original_module_len;
|
||||
let mut_glob_sl =
|
||||
r.gas_metered_mut_glob_then_stack_limited_len * 100 / r.original_module_len;
|
||||
|
||||
println!(
|
||||
"{filename:30}: orig = {original_size:4} kb, stack_limiter = {stack_limit} %, \
|
||||
gas_metered_host_fn = {host_fn} %, both = {host_fn_sl} %,\n \
|
||||
{:69} gas_metered_mut_global = {mut_glob} %, both = {mut_glob_sl} %",
|
||||
""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare module size overhead of applying gas metering with two methods.
|
||||
///
|
||||
/// Use `cargo test print_gas_metered_sizes -- --nocapture`.
|
||||
#[test]
|
||||
fn print_gas_metered_sizes() {
|
||||
let overheads = calc_size_overheads();
|
||||
let mut results = overheads
|
||||
.iter()
|
||||
.map(|r| {
|
||||
let diff = (r.gas_metered_mut_glob_len * 100 / r.gas_metered_host_fn_len) as i32 - 100;
|
||||
(diff, r)
|
||||
})
|
||||
.collect::<Vec<(i32, &InstrumentedWasmResults)>>();
|
||||
results.sort_unstable_by(|a, b| b.0.cmp(&a.0));
|
||||
|
||||
println!(
|
||||
"| {:28} | {:^16} | gas metered/host fn | gas metered/mut global | size diff |",
|
||||
"fixture", "original size",
|
||||
);
|
||||
println!("|{:-^30}|{:-^18}|{:-^21}|{:-^24}|{:-^11}|", "", "", "", "", "",);
|
||||
for r in results {
|
||||
let filename = &r.1.filename;
|
||||
let original_size = &r.1.original_module_len / 1024;
|
||||
let host_fn = &r.1.gas_metered_host_fn_len / 1024;
|
||||
let mut_glob = &r.1.gas_metered_mut_glob_len / 1024;
|
||||
let host_fn_percent = &r.1.gas_metered_host_fn_len * 100 / r.1.original_module_len;
|
||||
let mut_glob_percent = &r.1.gas_metered_mut_glob_len * 100 / r.1.original_module_len;
|
||||
let host_fn = format!("{host_fn} kb ({host_fn_percent:}%)");
|
||||
let mut_glob = format!("{mut_glob} kb ({mut_glob_percent:}%)");
|
||||
let diff = &r.0;
|
||||
println!(
|
||||
"| {filename:28} | {original_size:13} kb | {host_fn:>19} | {mut_glob:>22} | {diff:+8}% |"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user