Skip to content

Commit b38f311

Browse files
authored
GAS-1051: Gas time stats and visualization (to master) (filecoin-project#1259)
* GAS-1051: syscall (re-)pricing stats (filecoin-project#1054) * GAS-1150: Add timers to new charges. * GAS-1147: Visualize traces (filecoin-project#1148) * GAS-1160: Calibration contract (filecoin-project#1192)
1 parent 82c02a9 commit b38f311

File tree

97 files changed

+2260
-198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+2260
-198
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs:
6161
key: v3-cov
6262
covname: lcov.info
6363
command: llvm-cov
64-
args: --all --exclude fvm --exclude fvm_conformance_tests --exclude fvm_integration_tests --exclude "*actor" --lcov --output-path lcov.info
64+
args: --all --exclude fvm --exclude fvm_conformance_tests --exclude fvm_integration_tests --exclude fvm_gas_calibration --exclude "*actor" --lcov --output-path lcov.info
6565
components: llvm-tools-preview
6666
- name: integration
6767
key: v3

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ target
22
*.wat
33
*.wasm
44
!testing/integration/tests/assets/*
5+
testing/conformance/traces
56
.idea/
67
lcov.info

Cargo.lock

+67-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ members = [
66
"shared",
77
"testing/conformance",
88
"testing/integration",
9+
"testing/calibration",
910
"ipld/*",
10-
"testing/integration/tests/*-actor"
11+
"testing/integration/tests/*-actor",
12+
"testing/calibration/contract/*-actor"
1113
]
1214

1315
[profile.actor]

fvm/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ yastl = "0.1.2"
4242
arbitrary = { version = "1.1.0", optional = true, features = ["derive"] }
4343
rand = "0.8.5"
4444
quickcheck = { version = "1", optional = true }
45+
once_cell = "1.5"
46+
minstant = "0.1.2"
4547

4648
[dev-dependencies]
4749
pretty_assertions = "1.2.1"

fvm/src/call_manager/default.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{Backtrace, CallManager, InvocationResult, NO_DATA_BLOCK_ID};
1919
use crate::call_manager::backtrace::Frame;
2020
use crate::call_manager::FinishRet;
2121
use crate::engine::Engine;
22-
use crate::gas::{Gas, GasTracker};
22+
use crate::gas::{Gas, GasTimer, GasTracker};
2323
use crate::kernel::{Block, BlockRegistry, ExecutionError, Kernel, Result, SyscallError};
2424
use crate::machine::limiter::ExecMemory;
2525
use crate::machine::Machine;
@@ -309,6 +309,7 @@ where
309309
actor_id: ActorID,
310310
predictable_address: Option<Address>,
311311
) -> Result<()> {
312+
let start = GasTimer::start();
312313
// TODO https://github.com/filecoin-project/builtin-actors/issues/492
313314
let singleton = self.machine.builtin_actors().is_singleton_actor(&code_id);
314315

@@ -346,9 +347,10 @@ where
346347
// Create a new actor.
347348
None => (ActorState::new_empty(code_id, predictable_address), true),
348349
};
349-
self.charge_gas(self.price_list().on_create_actor(is_new))?;
350+
let t = self.charge_gas(self.price_list().on_create_actor(is_new))?;
350351
self.state_tree_mut().set_actor(actor_id, actor)?;
351352
self.num_actors_created += 1;
353+
t.stop_with(start);
352354
Ok(())
353355
}
354356

@@ -381,7 +383,7 @@ where
381383
where
382384
K: Kernel<CallManager = Self>,
383385
{
384-
self.charge_gas(self.price_list().on_create_actor(true))?;
386+
let t = self.charge_gas(self.price_list().on_create_actor(true))?;
385387

386388
if addr.is_bls_zero_address() {
387389
return Err(
@@ -408,6 +410,9 @@ where
408410
syscall_error!(IllegalArgument; "failed to serialize params: {}", e)
409411
})?;
410412

413+
// The cost of sending the message is measured independently.
414+
t.stop();
415+
411416
self.send_resolved::<K>(
412417
account_actor::SYSTEM_ACTOR_ID,
413418
id,
@@ -423,13 +428,13 @@ where
423428
where
424429
K: Kernel<CallManager = Self>,
425430
{
426-
self.charge_gas(self.price_list().on_create_actor(true))?;
431+
let t = self.charge_gas(self.price_list().on_create_actor(true))?;
427432

428433
// Create the actor in the state tree, but don't call any constructor.
429434
let code_cid = self.builtin_actors().get_embryo_code();
430435

431436
let state = ActorState::new_empty(*code_cid, Some(*addr));
432-
self.machine.create_actor(addr, state)
437+
t.record(self.machine.create_actor(addr, state))
433438
}
434439

435440
/// Send without checking the call depth.
@@ -485,7 +490,7 @@ where
485490
.ok_or_else(|| syscall_error!(NotFound; "actor does not exist: {}", to))?;
486491

487492
// Charge the method gas. Not sure why this comes second, but it does.
488-
self.charge_gas(self.price_list().on_method_invocation(value, method))?;
493+
let _ = self.charge_gas(self.price_list().on_method_invocation(value, method))?;
489494

490495
// Transfer, if necessary.
491496
if !value.is_zero() {

fvm/src/call_manager/mod.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use fvm_shared::error::ExitCode;
77
use fvm_shared::{ActorID, MethodNum};
88

99
use crate::engine::Engine;
10-
use crate::gas::{Gas, GasCharge, GasTracker, PriceList};
10+
use crate::gas::{Gas, GasCharge, GasTimer, GasTracker, PriceList};
1111
use crate::kernel::{self, Result};
1212
use crate::machine::{Machine, MachineContext};
1313
use crate::state_tree::StateTree;
@@ -147,9 +147,8 @@ pub trait CallManager: 'static {
147147
}
148148

149149
/// Charge gas.
150-
fn charge_gas(&self, charge: GasCharge) -> Result<()> {
151-
self.gas_tracker().apply_charge(charge)?;
152-
Ok(())
150+
fn charge_gas(&self, charge: GasCharge) -> Result<GasTimer> {
151+
self.gas_tracker().apply_charge(charge)
153152
}
154153

155154
/// Limit memory usage throughout a message execution.

fvm/src/engine.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ use wasmtime::{
1616
Module, Mutability, PoolingAllocationStrategy, Val, ValType,
1717
};
1818

19-
use crate::gas::WasmGasPrices;
19+
use crate::gas::{GasTimer, WasmGasPrices};
2020
use crate::machine::limiter::ExecMemory;
2121
use crate::machine::{Machine, NetworkConfig};
22-
use crate::syscalls::{bind_syscalls, charge_for_init, InvocationData};
22+
use crate::syscalls::{bind_syscalls, charge_for_init, record_init_time, InvocationData};
2323
use crate::Kernel;
2424

2525
/// Container managing engines with different consensus-affecting configurations.
@@ -508,9 +508,15 @@ impl Engine {
508508
// pay for the minimum memory requirements. The module instrumentation in `inject` only
509509
// adds code to charge for _growing_ the memory, but not for the amount made accessible
510510
// initially. The limits are checked by wasmtime during instantiation, though.
511-
charge_for_init(store, module)?;
511+
let t = charge_for_init(store, module)?;
512512

513-
let inst = cache.linker.instantiate(store, module)?;
513+
let inst = cache.linker.instantiate(&mut *store, module)?;
514+
515+
// Record the time it took for the linker to instantiate the module.
516+
// This should also include everything that happens above in this method.
517+
// Note that this does _not_ contain the time it took the load the Wasm file,
518+
// which could have been cached already.
519+
record_init_time(store, t);
514520

515521
Ok(Some(inst))
516522
};
@@ -541,6 +547,7 @@ impl Engine {
541547
avail_gas_global: self.0.dummy_gas_global,
542548
last_milligas_available: 0,
543549
last_memory_bytes: memory_bytes,
550+
last_charge_time: GasTimer::start(),
544551
memory: self.0.dummy_memory,
545552
};
546553

fvm/src/executor/default.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ where
129129

130130
// Charge for including the result (before we end the transaction).
131131
if let Some(value) = &ret.value {
132-
cm.charge_gas(
132+
let _ = cm.charge_gas(
133133
cm.context()
134134
.price_list
135135
.on_chain_return_value(value.size() as usize),

fvm/src/gas/charge.rs

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::borrow::Cow;
66

7+
use super::timer::GasDuration;
78
use super::Gas;
89

910
/// Single gas charge in the VM. Contains information about what gas was for, as well
@@ -15,6 +16,8 @@ pub struct GasCharge {
1516
pub compute_gas: Gas,
1617
/// Storage costs
1718
pub storage_gas: Gas,
19+
/// Execution time related to this charge, if traced and successfully measured.
20+
pub elapsed: GasDuration,
1821
}
1922

2023
impl GasCharge {
@@ -24,6 +27,7 @@ impl GasCharge {
2427
name,
2528
compute_gas,
2629
storage_gas,
30+
elapsed: GasDuration::default(),
2731
}
2832
}
2933

fvm/src/gas/mod.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ use num_traits::Zero;
1111
pub use self::charge::GasCharge;
1212
pub(crate) use self::outputs::GasOutputs;
1313
pub use self::price_list::{price_list_by_network_version, PriceList, WasmGasPrices};
14+
pub use self::timer::{GasInstant, GasTimer};
1415
use crate::kernel::{ExecutionError, Result};
1516

1617
mod charge;
1718
mod outputs;
1819
mod price_list;
20+
mod timer;
1921

2022
pub const MILLIGAS_PRECISION: i64 = 1000;
2123

@@ -211,24 +213,31 @@ impl GasTracker {
211213

212214
/// Safely consumes gas and returns an out of gas error if there is not sufficient
213215
/// enough gas remaining for charge.
214-
pub fn charge_gas(&self, name: &str, to_use: Gas) -> Result<()> {
216+
pub fn charge_gas(&self, name: &str, to_use: Gas) -> Result<GasTimer> {
215217
log::trace!("charging gas: {} {}", name, to_use);
218+
let res = self.charge_gas_inner(to_use);
216219
if let Some(trace) = &self.trace {
217-
trace
218-
.borrow_mut()
219-
.push(GasCharge::new(name.to_owned(), to_use, Gas::zero()))
220+
let mut charge = GasCharge::new(name.to_owned(), to_use, Gas::zero());
221+
let timer = GasTimer::new(&mut charge.elapsed);
222+
trace.borrow_mut().push(charge);
223+
res.map(|_| timer)
224+
} else {
225+
res.map(|_| GasTimer::empty())
220226
}
221-
self.charge_gas_inner(to_use)
222227
}
223228

224229
/// Applies the specified gas charge, where quantities are supplied in milligas.
225-
pub fn apply_charge(&self, charge: GasCharge) -> Result<()> {
230+
pub fn apply_charge(&self, mut charge: GasCharge) -> Result<GasTimer> {
226231
let to_use = charge.total();
227232
log::trace!("charging gas: {} {}", &charge.name, to_use);
233+
let res = self.charge_gas_inner(to_use);
228234
if let Some(trace) = &self.trace {
235+
let timer = GasTimer::new(&mut charge.elapsed);
229236
trace.borrow_mut().push(charge);
237+
res.map(|_| timer)
238+
} else {
239+
res.map(|_| GasTimer::empty())
230240
}
231-
self.charge_gas_inner(to_use)
232241
}
233242

234243
/// Absorbs another GasTracker (usually a nested one) into this one, charging for gas
@@ -293,9 +302,9 @@ mod tests {
293302
#[allow(clippy::identity_op)]
294303
fn basic_gas_tracker() -> Result<()> {
295304
let t = GasTracker::new(Gas::new(20), Gas::new(10), false);
296-
t.apply_charge(GasCharge::new("", Gas::new(5), Gas::zero()))?;
305+
let _ = t.apply_charge(GasCharge::new("", Gas::new(5), Gas::zero()))?;
297306
assert_eq!(t.gas_used(), Gas::new(15));
298-
t.apply_charge(GasCharge::new("", Gas::new(5), Gas::zero()))?;
307+
let _ = t.apply_charge(GasCharge::new("", Gas::new(5), Gas::zero()))?;
299308
assert_eq!(t.gas_used(), Gas::new(20));
300309
assert!(t
301310
.apply_charge(GasCharge::new("", Gas::new(1), Gas::zero()))

0 commit comments

Comments
 (0)