Skip to content

Commit

Permalink
use new metering as the default (#53)
Browse files Browse the repository at this point in the history
* use new metering as the default

* fix

* bump candid
  • Loading branch information
chenyan-dfinity committed Nov 28, 2023
1 parent 9cfc86c commit 61692f4
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 266 deletions.
255 changes: 88 additions & 167 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ic-wasm"
version = "0.6.1"
version = "0.7.0"
authors = ["DFINITY Stiftung"]
edition = "2021"
description = "A library for performing Wasm transformations specific to canisters running on the Internet Computer"
Expand All @@ -19,7 +19,7 @@ required-features = ["exe"]

[dependencies]
walrus = "0.20.1"
candid = "0.9.9"
candid = "0.10"
rustc-demangle = "0.1"
thiserror = "1.0.35"

Expand All @@ -31,7 +31,7 @@ serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }

[features]
default = ["anyhow", "clap", "wasm-opt"]
default = ["exe", "wasm-opt"]
exe = ["anyhow", "clap", "serde"]
wasm-opt = ["dep:wasm-opt", "tempfile"]
serde = ["dep:serde", "dep:serde_json"]
Expand Down
5 changes: 0 additions & 5 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ enum SubCommand {
/// The number of pages of the preallocated stable memory
#[clap(short, long, requires("start_page"))]
page_limit: Option<i32>,
/// Use the new metering cost, default to false. This flag will eventually be removed and set to true after the mainnet has adapted the new metering.
#[clap(short, long)]
use_new_metering: bool,
},
}

Expand Down Expand Up @@ -126,14 +123,12 @@ fn main() -> anyhow::Result<()> {
trace_only,
start_page,
page_limit,
use_new_metering,
} => {
use ic_wasm::instrumentation::{instrument, Config};
let config = Config {
trace_only_funcs: trace_only.clone().unwrap_or(vec![]),
start_address: start_page.map(|page| page * 65536),
page_limit: *page_limit,
use_new_metering: *use_new_metering,
};
instrument(&mut m, config).map_err(|e| anyhow::anyhow!("{e}"))?;
}
Expand Down
23 changes: 9 additions & 14 deletions src/instrumentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ pub struct Config {
pub trace_only_funcs: Vec<String>,
pub start_address: Option<i32>,
pub page_limit: Option<i32>,
pub use_new_metering: bool,
}
impl Config {
pub fn is_preallocated(&self) -> bool {
Expand Down Expand Up @@ -69,7 +68,7 @@ pub fn instrument(m: &mut Module, config: Config) -> Result<(), String> {
trace_only_ids.insert(id);
}
let is_partial_tracing = !trace_only_ids.is_empty();
let func_cost = FunctionCost::new(m, config.use_new_metering);
let func_cost = FunctionCost::new(m);
let total_counter = m
.globals
.add_local(ValType::I64, true, InitExpr::Value(Value::I64(0)));
Expand Down Expand Up @@ -110,7 +109,6 @@ pub fn instrument(m: &mut Module, config: Config) -> Result<(), String> {
&vars,
&func_cost,
is_partial_tracing,
config.use_new_metering,
);
}
}
Expand Down Expand Up @@ -151,7 +149,6 @@ fn inject_metering(
vars: &Variables,
func_cost: &FunctionCost,
is_partial_tracing: bool,
use_new_metering: bool,
) {
use InjectionKind::*;
let mut stack = vec![start];
Expand All @@ -161,17 +158,15 @@ fn inject_metering(
let mut injection_points = vec![];
let mut curr = InjectionPoint::new();
// each function has at least a unit cost
if seq_id == start && use_new_metering {
if seq_id == start {
curr.cost += 1;
}
for (pos, (instr, _)) in seq.instrs.iter().enumerate() {
curr.position = pos;
match instr {
Instr::Block(Block { seq }) | Instr::Loop(Loop { seq }) => {
match func.block(*seq).ty {
InstrSeqType::Simple(Some(_)) => {
curr.cost += instr_cost(instr, use_new_metering)
}
InstrSeqType::Simple(Some(_)) => curr.cost += instr_cost(instr),
InstrSeqType::Simple(None) => (),
InstrSeqType::MultiValue(_) => unreachable!("Multivalue not supported"),
}
Expand All @@ -183,25 +178,25 @@ fn inject_metering(
consequent,
alternative,
}) => {
curr.cost += instr_cost(instr, use_new_metering);
curr.cost += instr_cost(instr);
stack.push(*consequent);
stack.push(*alternative);
injection_points.push(curr);
curr = InjectionPoint::new();
}
Instr::Br(_) | Instr::BrIf(_) | Instr::BrTable(_) => {
// br always points to a block, so we don't need to push the br block to stack for traversal
curr.cost += instr_cost(instr, use_new_metering);
curr.cost += instr_cost(instr);
injection_points.push(curr);
curr = InjectionPoint::new();
}
Instr::Return(_) | Instr::Unreachable(_) => {
curr.cost += instr_cost(instr, use_new_metering);
curr.cost += instr_cost(instr);
injection_points.push(curr);
curr = InjectionPoint::new();
}
Instr::Call(Call { func }) => {
curr.cost += instr_cost(instr, use_new_metering);
curr.cost += instr_cost(instr);
match func_cost.get_cost(*func) {
Some((cost, InjectionKind::Static)) => curr.cost += cost,
Some((cost, kind @ InjectionKind::Dynamic))
Expand All @@ -222,7 +217,7 @@ fn inject_metering(
| Instr::MemoryInit(_)
| Instr::TableCopy(_)
| Instr::TableInit(_) => {
curr.cost += instr_cost(instr, use_new_metering);
curr.cost += instr_cost(instr);
let dynamic = InjectionPoint {
position: pos,
cost: 0,
Expand All @@ -231,7 +226,7 @@ fn inject_metering(
injection_points.push(dynamic);
}
_ => {
curr.cost += instr_cost(instr, use_new_metering);
curr.cost += instr_cost(instr);
}
}
}
Expand Down
113 changes: 44 additions & 69 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub(crate) enum InjectionKind {

pub(crate) struct FunctionCost(HashMap<FunctionId, (i64, InjectionKind)>);
impl FunctionCost {
pub fn new(m: &Module, use_new_metering: bool) -> Self {
pub fn new(m: &Module) -> Self {
let mut res = HashMap::new();
for (method, func) in m.imports.iter().filter_map(|i| {
if let ImportKind::Function(func) = i.kind {
Expand All @@ -47,70 +47,48 @@ impl FunctionCost {
}) {
use InjectionKind::*;
// System API cost taken from https://github.com/dfinity/ic/blob/master/rs/embedders/src/wasmtime_embedder/system_api_complexity.rs
let cost = if use_new_metering {
match method {
"accept_message" => (500, Static),
"call_cycles_add" | "call_cycles_add128" => (500, Static),
"call_data_append" => (500, Dynamic),
"call_new" => (1500, Static),
"call_on_cleanup" => (500, Static),
"call_perform" => (5000, Static),
"canister_cycle_balance" | "canister_cycle_balance128" => (500, Static),
"canister_self_copy" => (500, Dynamic),
"canister_self_size" => (500, Static),
"canister_status" | "canister_version" => (500, Static),
"certified_data_set" => (500, Dynamic),
"data_certificate_copy" => (500, Dynamic),
"data_certificate_present" | "data_certificate_size" => (500, Static),
"debug_print" => (100, Dynamic),
"global_timer_set" => (500, Static),
"is_controller" => (1000, Dynamic),
"msg_arg_data_copy" => (500, Dynamic),
"msg_arg_data_size" => (500, Static),
"msg_caller_copy" => (500, Dynamic),
"msg_caller_size" => (500, Static),
"msg_cycles_accept" | "msg_cycles_accept128" => (500, Static),
"msg_cycles_available" | "msg_cycles_available128" => (500, Static),
"msg_cycles_refunded" | "msg_cycles_refunded128" => (500, Static),
"cycles_burn128" => (100, Static),
"msg_method_name_copy" => (500, Dynamic),
"msg_method_name_size" => (500, Static),
"msg_reject_code" | "msg_reject_msg_size" => (500, Static),
"msg_reject_msg_copy" => (500, Dynamic),
"msg_reject" => (500, Dynamic),
"msg_reply_data_append" => (500, Dynamic),
"msg_reply" => (500, Static),
"performance_counter" => (200, Static),
"stable_grow" | "stable64_grow" => (100, Static),
"stable_size" | "stable64_size" => (20, Static),
"stable_read" => (20, Dynamic),
"stable_write" => (20, Dynamic),
"stable64_read" => (20, Dynamic64),
"stable64_write" => (20, Dynamic64),
"trap" => (500, Dynamic),
"time" => (500, Static),
_ => (20, Static),
}
} else {
match method {
"msg_arg_data_copy" => (20, Dynamic),
"msg_method_name_copy" => (20, Dynamic),
"msg_reply_data_append" => (20, Dynamic),
"msg_reject" => (20, Dynamic),
"msg_reject_msg_copy" => (20, Dynamic),
"debug_print" => (100, Dynamic),
"is_controller" => (1000, Dynamic),
"trap" => (20, Dynamic),
"call_new" => (0, Static),
"call_data_append" => (20, Dynamic),
"call_perform" => (0, Static),
"stable_read" => (20, Dynamic),
"stable_write" => (20, Dynamic),
"stable64_read" => (20, Dynamic64),
"stable64_write" => (20, Dynamic64),
"performance_counter" => (200, Static),
_ => (0, Static),
}
let cost = match method {
"accept_message" => (500, Static),
"call_cycles_add" | "call_cycles_add128" => (500, Static),
"call_data_append" => (500, Dynamic),
"call_new" => (1500, Static),
"call_on_cleanup" => (500, Static),
"call_perform" => (5000, Static),
"canister_cycle_balance" | "canister_cycle_balance128" => (500, Static),
"canister_self_copy" => (500, Dynamic),
"canister_self_size" => (500, Static),
"canister_status" | "canister_version" => (500, Static),
"certified_data_set" => (500, Dynamic),
"data_certificate_copy" => (500, Dynamic),
"data_certificate_present" | "data_certificate_size" => (500, Static),
"debug_print" => (100, Dynamic),
"global_timer_set" => (500, Static),
"is_controller" => (1000, Dynamic),
"msg_arg_data_copy" => (500, Dynamic),
"msg_arg_data_size" => (500, Static),
"msg_caller_copy" => (500, Dynamic),
"msg_caller_size" => (500, Static),
"msg_cycles_accept" | "msg_cycles_accept128" => (500, Static),
"msg_cycles_available" | "msg_cycles_available128" => (500, Static),
"msg_cycles_refunded" | "msg_cycles_refunded128" => (500, Static),
"cycles_burn128" => (100, Static),
"msg_method_name_copy" => (500, Dynamic),
"msg_method_name_size" => (500, Static),
"msg_reject_code" | "msg_reject_msg_size" => (500, Static),
"msg_reject_msg_copy" => (500, Dynamic),
"msg_reject" => (500, Dynamic),
"msg_reply_data_append" => (500, Dynamic),
"msg_reply" => (500, Static),
"performance_counter" => (200, Static),
"stable_grow" | "stable64_grow" => (100, Static),
"stable_size" | "stable64_size" => (20, Static),
"stable_read" => (20, Dynamic),
"stable_write" => (20, Dynamic),
"stable64_read" => (20, Dynamic64),
"stable64_write" => (20, Dynamic64),
"trap" => (500, Dynamic),
"time" => (500, Static),
_ => (20, Static),
};
res.insert(func, cost);
}
Expand All @@ -120,13 +98,10 @@ impl FunctionCost {
self.0.get(&id).copied()
}
}
pub(crate) fn instr_cost(i: &ir::Instr, use_new_metering: bool) -> i64 {
pub(crate) fn instr_cost(i: &ir::Instr) -> i64 {
use ir::*;
use BinaryOp::*;
use UnaryOp::*;
if !use_new_metering {
return 1;
}
// Cost taken from https://github.com/dfinity/ic/blob/master/rs/embedders/src/wasm_utils/instrumentation.rs
match i {
Instr::Block(..) | Instr::Loop(..) => 0,
Expand Down
16 changes: 8 additions & 8 deletions tests/deployable.ic-repl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,33 +98,33 @@ function check_profiling(S, cycles, len) {
};

let S = counter(file("ok/motoko-instrument.wasm"));
check_profiling(S, 9397, 78);
check_profiling(S, 21571, 78);
let S = counter(file("ok/motoko-gc-instrument.wasm"));
check_profiling(S, 250, 4);
check_profiling(S, 595, 4);
let wasm = file("ok/motoko-region-instrument.wasm");
let S = counter(wasm);
check_profiling(S, 463666, 78);
check_profiling(S, 478652, 78);
upgrade(S, wasm);
call S.get();
assert _ == (45 : nat);
check_profiling(S, 474294, 460);
check_profiling(S, 494682, 460);
counter(file("ok/motoko-shrink.wasm"));
counter(file("ok/motoko-limit.wasm"));

let S = counter(file("ok/rust-instrument.wasm"));
check_profiling(S, 53149, 576);
check_profiling(S, 75279, 576);
let wasm = file("ok/rust-region-instrument.wasm");
let S = counter(wasm);
check_profiling(S, 126136, 574);
check_profiling(S, 152042, 574);
upgrade(S, wasm);
call S.get();
assert _ == (45 : nat);
check_profiling(S, 911310, 2344);
check_profiling(S, 1023168, 2344);
counter(file("ok/rust-shrink.wasm"));
counter(file("ok/rust-limit.wasm"));

let S = wat(file("ok/wat-instrument.wasm"));
check_profiling(S, 189, 2);
check_profiling(S, 5656, 2);
wat(file("ok/wat-shrink.wasm"));
wat(file("ok/wat-limit.wasm"));

Expand Down
Binary file modified tests/ok/motoko-gc-instrument.wasm
Binary file not shown.
Binary file modified tests/ok/motoko-instrument.wasm
Binary file not shown.
Binary file modified tests/ok/motoko-region-instrument.wasm
Binary file not shown.
Binary file modified tests/ok/rust-instrument.wasm
Binary file not shown.
Binary file modified tests/ok/rust-region-instrument.wasm
Binary file not shown.
Binary file modified tests/ok/wat-instrument.wasm
Binary file not shown.

0 comments on commit 61692f4

Please sign in to comment.