-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
The pallet_staking_async benchmarks show a ~75-115x regression in execution time (from ~40µs to
~3-5ms) for several extrinsics e.g. see here.
This is not a real runtime regression but rather a benchmarking artifact caused by the interaction between the genesis state and the benchmark setup.
The genesis configuration in genesis_config_presets.rs pre-populates a large number of stakers for testing purposes:
"staking": {
"validatorCount": 600,
"devStakers": Some((2_000, 25_000)), // 2,000 validators + 25,000 nominators = 27,000 stakers
},(similar for e.g. Westend AH with 1k validators and 25k nominators).
Many benchmarks call clear_validators_and_nominators() in their setup phase to ensure a clean state. While this happens in "setup" (before timing starts), the benchmark framework's commit_db() and storage root recalculation costs leak into the measured benchmark time when clearing 27,000 storage entries.
Affected Benchmarks
All benchmarks that call clear_validators_and_nominators() are affected:
| Benchmark | Before | After | Change |
|---|---|---|---|
force_apply_min_commission |
42.8 µs | 4,886 µs | +11,318% |
rebond |
64.4 µs | 5,126 µs | +7,858% |
chill |
44.5 µs | 5,043 µs | +11,232% |
chill_other |
46.1 µs | 5,043 µs | +10,839% |
unbond |
64.0 µs | 5,254 µs | +8,109% |
bond_extra |
91.4 µs | 5,255 µs | +5,649% |
reap_stash |
60.9 µs | 5,033 µs | +8,164% |
force_unstake |
61.1 µs | 5,054 µs | +8,172% |
withdraw_unbonded_kill |
65.0 µs | 5,156 µs | +7,832% |
nominate |
82.7 µs | 5,107 µs | +6,076% |
process_offence_queue |
76.4 µs | 4,951 µs | +6,381% |
Tested approaches
Verification
Disable proof recording
- ❌
--disable-proof-recordingalone - does not fix the issue
Genesis build none
- ✅
--genesis-builder none- fixes the issue, benchmarks return to µs range
Force a sleep at the end of commit_db
See #10802 (comment)
No luck, still ms
Force a dummy read at the end of commit_db
No luck, still ms
Force storage root recalculation at the end of commit_db
fn commit_db(&mut self) {
self.commit();
let _ = self.storage_root(sp_runtime::StateVersion::V1);
}no luck, still ms
Force commit db at the end of clear_validators_and_nominators
if we just want to unlock staking very quickly, the easiest seems just adding
frame_benchmarking::benchmarking::commit_db();at the end of the infamous clear_validators_and_nominators(). Then we can open a SDK issue and work in background to fix the real problem of expensive root calculation leaking into benchmark timing.
Tested and works just fine for e.g. force_apply_min_commission and process_offence_queue.
NOTE: note that in the benchmark framework we are calling already commit_db() when calling Recording.start() here which then executes this
Rework staking benchmarks to avoid massive bulk deletion if not needed
An example showing the validity of this approach here where we just remove a clear_validators_and_nominators() from one benchmark and that's enough to go down from ms to microsec).
Related issue: #10813 -> I believe we should both fix the problem in the framework and improve staking setup
Steps to reproduce
Steps to reproduce:
- build WAH runtime
cargo build --release -p asset-hub-westend-runtime --features try-runtime,runtime-benchmarks- run benchmark test of one of the affected extrinsics e.g.
frame-omni-bencher v1 benchmark pallet --runtime ./target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_
runtime.compact.compressed.wasm --pallet pallet_staking_async --extrinsic "force_apply_min_commission" --steps 50 --repeat 20Expected result:
- execution time in few µs (simple exstrinsic with 2 storage read, 1 storage write!!!)
Actual result: - execution time in few ms