-
Notifications
You must be signed in to change notification settings - Fork 2.6k
contracts: switch to wasmi gas metering #14084
Conversation
save: compiles, only gas pushing left to macro WIP proc macro proc macro: done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the right direction just a few important points:
- We need a more elegant way to pass around the consumed fuel. Don't add it to an exposed interface type (see my comment).
- We really should not cache any values anymore here. I am talking about memory min and max values. We really only want the
PristineCode
andOwnerInfo
in-storage. ThePrefabWasmModule
should only be constructed in-memory.
tests::run_out_of_gas_engine, need 2 more save: 2 bugs with gas syncs: 1 of 2 tests done gas_syncs_no_overcharge bug fixed, test passes! cleaned out debug prints second bug is not a bug disabled_chain_extension test fix (err msg) tests run_out_of_fuel_host, chain_extension pass all tests pass
@athei @pgherveou addressed all but the migration change which is in progress... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good for me once all the comments are addressed and I think you missed some from Alex too
bot bench $ pallet dev pallet_contracts |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
bot merge |
What I mean by this is that benchmarks for all host functions should not now account for weight spent on the engine side, as it is being accounted for with wasmi built-in fuel meter. Otherwise we charge for the engine spent part twice. tl;dr: but this overhead seems to be negligible anyway, see below for the details ExampleLet's consider for example the wat code(module
(type (;0;) (func))
(type (;1;) (func (param i32 i32 i64 i64 i32 i32 i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory (;0;) 16 16))
(import "seal2" "call" (func (;0;) (type 1)))
(func (;1;) (type 0))
(func (;2;) (type 0)
i32.const 0
i32.const 24
i64.const 0
i64.const 0
i32.const 8
i32.const 0
i32.const 0
i32.const 0
i32.const -1
i32.const 0
call 0
drop
i32.const 0
i32.const 56
i64.const 0
i64.const 0
i32.const 16
i32.const 0
i32.const 0
i32.const 0
i32.const -1
i32.const 0
call 0
drop)
(export "deploy" (func 1))
(export "call" (func 2))
(data (;0;) (i32.const 0) "\00\00\00\00\00\00\00\00")
(data (;1;) (i32.const 8) "\00\00\00\00\00\00\00\00\9b\ff\ff\ff\00\00\00\00")
(data (;2;) (i32.const 24) "\92\e2\bf\1c\db\9a\b6\8d\bf|\92\8f\8dl\03!\ee\adCL!\a47'.\0f\93\07s\087\eb\fb\a3@\9c\16dX+\e4\e7E\8e\b3\d0\ba>\051\a9\d5\c33\d9\b3\dcd\c7!\86\d6\db\92")) Here for simplicity I gave the example for Every time we call our host function, we add this auxiliary set of 12 instructions: i32.const 0
i32.const 24
i64.const 0
i64.const 0
i32.const 8
i32.const 0
i32.const 0
i32.const 0
i32.const -1
i32.const 0
call 0
drop Each of these instructions cost That being said though, this overhead is negligible compared to the host function weight: |
* upgrade to wasmi 0.29 * prepare cleanup * sync ref_time w engine from the stack frame * proc_macro: sync gas in host funcs save: compiles, only gas pushing left to macro WIP proc macro proc macro: done * clean benchmarks & schedule: w_base = w_i64const * scale gas values btw engine and gas meter * (re)instrumentation & code_cache removed * remove gas() host fn, continue clean-up save * address review comments * move from CodeStorage&PrefabWasmModule to PristineCode&WasmBlob * refactor: no reftime_limit&schedule passes, no CodeStorage * bugs fixing * fix tests: expected deposit amount * fix prepare::tests * update tests and fix bugs tests::run_out_of_gas_engine, need 2 more save: 2 bugs with gas syncs: 1 of 2 tests done gas_syncs_no_overcharge bug fixed, test passes! cleaned out debug prints second bug is not a bug disabled_chain_extension test fix (err msg) tests run_out_of_fuel_host, chain_extension pass all tests pass * update docs * bump wasmi 0.30.0 * benchmarks updated, tests pass * refactoring * s/OwnerInfo/CodeInfo/g; * migration: draft, compiles * migration: draft, runs * migration: draft, runs (fixing) * deposits repaid non pro rata * deposits repaid pro rata * better try-runtime output * even better try-runtime output * benchmark migration * fix merge leftover * add forgotten fixtures, fix docs * address review comments * ci fixes * cleanup * benchmarks::prepare to return DispatchError * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * store memory limits to CodeInfo * ci: roll back weights * ".git/.scripts/commands/bench-vm/bench-vm.sh" pallet dev pallet_contracts * drive-by: update Readme and pallet rustdoc * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * use wasmi 0.29 * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * use wasmi 0.30 again * query memory limits from wasmi * better migration types * ci: pull weights from master * refactoring * ".git/.scripts/commands/bench-vm/bench-vm.sh" pallet dev pallet_contracts * addressing review comments * refactor * address review comments * optimize migration * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * another review round comments addressed * ci fix one * clippy fix * ci fix two --------- Co-authored-by: command-bot <>
Closes #13639.
Summary
In order to switch to wasmi built-in fuel metering, the following is done:
(See Migration section for details.)
Except the
i64.const
instruction which is used as the base (see Fuel Syncs section).It whole was in place to guarantee the contract being executed is instrumented with the latest Schedule.
gas
host function, as well as the restriction to import a function with this name inside the Wasm module of a contract.Fuel Syncs
We have now two separate fuel meters, each having its own units of measurement:
Weight
burn during runtime host function execution.Units of fuel consumption in wasmi are normalized, so that basic operations like
i64.const
cost 1 Fuel. For conversion between the two UoM, theref_time
component ofi64.const
instruction Weight is used. This is why we keep its benchmark and its weight in the ScheduleWe do fuel consumption level synchronizations at the times of execution context switching, namely:
Migration
Storage migration incurred by this change does the following:
CodeStorage
with all instrumented contract codes.The only one we need is
PristineCode
with the original uploaded (and validated) codes.OwnerInfo
intoCodeInfo
, which incurs moving it to anotherStorageMap
.We store code related "meta" data in a separate storage item, which now contains not only owner-related fields. Hence the rename.
The amount due to refund is calculated pro rata to the currently held deposit.
Effect
Major effects are on storage usage efficiency (as we don't store instrumented code anymore), and performance improvements (as we get rid of (re-)instrumentation during on-chain execution, and fuel metering on the engine side works faster). Both things imply cost savings for contract authors and contract users.
Storage Usage & Deposits
The migration has been tested on Rococo with
try-runtime
, showing the following outcomes:Performance
See Add benchmarks for wasmi builtin metering wasm-instrument#56 for the benchmark results showing performance advantage of utilizing wasmi builtin fuel metering versus instrumentation approach.
Summary of new weights for the pallet can be found in this comment below.
Follow-up tasks:
wasm-instrument
/parity-wasm
dependency (contracts: switch fromparity-wasm
-based towasmi
-based module validation #14449)