Skip to content

Commit 804e113

Browse files
authored
Merge pull request #2380 from CosmWasm/co/metering-benchmark
Add loop benchmark
2 parents 55290a1 + cb1f0e0 commit 804e113

File tree

4 files changed

+58
-13
lines changed

4 files changed

+58
-13
lines changed

contracts/cyberpunk/src/contract.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,8 @@ fn execute_argon2(mem_cost: u32, time_cost: u32) -> Result<Response, ContractErr
6565
}
6666

6767
fn execute_cpu_loop() -> Result<Response, ContractError> {
68-
let mut counter = 0u64;
69-
loop {
70-
counter += 1;
71-
if counter >= 9_000_000_000 {
72-
counter = 0;
73-
}
74-
}
68+
#[allow(clippy::empty_loop)]
69+
loop {}
7570
}
7671

7772
fn execute_storage_loop(deps: DepsMut) -> Result<Response, ContractError> {

packages/vm/benches/main.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use criterion::{black_box, criterion_group, criterion_main, Criterion};
1+
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
22

33
use rand::Rng;
44
use std::sync::Arc;
@@ -12,7 +12,7 @@ use cosmwasm_vm::testing::{
1212
};
1313
use cosmwasm_vm::{
1414
call_execute, call_instantiate, capabilities_from_csv, Cache, CacheOptions, Instance,
15-
InstanceOptions, Size,
15+
InstanceOptions, Size, VmError,
1616
};
1717

1818
// Instance
@@ -22,6 +22,7 @@ const DEFAULT_INSTANCE_OPTIONS: InstanceOptions = InstanceOptions {
2222
gas_limit: DEFAULT_GAS_LIMIT,
2323
};
2424
const HIGH_GAS_LIMIT: u64 = 20_000_000_000_000; // ~20s, allows many calls on one instance
25+
const MEDIUM_GAS_LIMIT: u64 = 1_000_000_000_000; // ~1s
2526

2627
// Cache
2728
const MEMORY_CACHE_SIZE: Size = Size::mebi(200);
@@ -133,6 +134,48 @@ fn bench_instance(c: &mut Criterion) {
133134
println!("Gas used: {gas_used}");
134135
});
135136

137+
group.bench_function("execute execute (infinite loop)", |b| {
138+
let backend = mock_backend(&[]);
139+
let medium_gas: InstanceOptions = InstanceOptions {
140+
gas_limit: MEDIUM_GAS_LIMIT,
141+
};
142+
let mut instance =
143+
Instance::from_code(CYBERPUNK, backend, medium_gas, Some(DEFAULT_MEMORY_LIMIT))
144+
.unwrap();
145+
146+
let info = mock_info("creator", &coins(1000, "earth"));
147+
let contract_result =
148+
call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, b"{}").unwrap();
149+
assert!(contract_result.into_result().is_ok());
150+
151+
let mut gas_used = 0;
152+
b.iter_batched(
153+
|| {
154+
// setup new instance for each iteration because cpu loop will consume all gas
155+
Instance::from_code(
156+
CYBERPUNK,
157+
mock_backend(&[]),
158+
medium_gas,
159+
Some(DEFAULT_MEMORY_LIMIT),
160+
)
161+
.unwrap()
162+
},
163+
|mut instance| {
164+
let gas_before = instance.get_gas_left();
165+
let info = mock_info("hasher", &[]);
166+
let msg = br#"{"cpu_loop":{}}"#;
167+
168+
let vm_result =
169+
call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg);
170+
171+
assert!(matches!(vm_result, Err(VmError::GasDepletion { .. })));
172+
gas_used = gas_before - instance.get_gas_left();
173+
},
174+
BatchSize::SmallInput,
175+
);
176+
println!("Gas used: {gas_used}");
177+
});
178+
136179
group.finish();
137180
}
138181

packages/vm/src/wasm_backend/engine.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use super::limiting_tunables::LimitingTunables;
1717
/// https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md
1818
const MAX_WASM_PAGES: u32 = 65536;
1919

20+
// This function is hashed and put into the `module_version_discriminator` because it is used as
21+
// part of the compilation process. If it changes, modules need to be recompiled.
2022
#[hash_function(const_name = "COST_FUNCTION_HASH")]
2123
fn cost(operator: &Operator) -> u64 {
2224
// A flat fee for each operation
@@ -25,13 +27,18 @@ fn cost(operator: &Operator) -> u64 {
2527
// In https://github.com/CosmWasm/cosmwasm/pull/1042 a profiler is developed to
2628
// identify runtime differences between different Wasm operation, but this is not yet
2729
// precise enough to derive insights from it.
28-
//
29-
// Please note that any changes to this function need to be accompanied by a bump of
30-
// `MODULE_SERIALIZATION_VERSION` to avoid cached modules from using different amounts of gas
31-
// compared to newly compiled ones.
3230
const GAS_PER_OPERATION: u64 = 115;
3331

3432
if is_accounting(operator) {
33+
// Accounting operators are operators where the `Metering` middleware injects instructions
34+
// to count the gas usage and check for gas exhaustion. Therefore they are more expensive.
35+
//
36+
// Benchmarks show that the overhead is about 14 times the cost of a normal operation.
37+
// To benchmark this, set `GAS_PER_OPERATION = 100` and run the "infinite loop" and
38+
// "argon2" benchmarks. From the "Gas used" output, you can calculate the number of
39+
// operations and from that together with the run time the expected gas value per operation:
40+
// GAS_PER_OP = GAS_TARGET_PER_SEC / (NUM_OPS / RUNTIME_IN_SECS)
41+
// This is repeated with different multipliers to bring the two benchmarks closer together.
3542
GAS_PER_OPERATION * 14
3643
} else {
3744
GAS_PER_OPERATION

packages/vm/testdata/cyberpunk.wasm

-5.69 KB
Binary file not shown.

0 commit comments

Comments
 (0)