Rust区块链框架FRAME插件frame-system-benchmarking的使用:Substrate系统模块性能基准测试工具
// 示例代码:frame-system-benchmarking 基准测试配置
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_system::RawOrigin;
benchmarks! {
// 基准测试:设置代码哈希
set_code {
let caller = whitelisted_caller();
}: _(RawOrigin::Signed(caller))
// 基准测试:设置代码哈希无权限检查
set_code_without_checks {
let caller = whitelisted_caller();
}: _(RawOrigin::Signed(caller))
// 基准测试:设置存储项
set_storage {
let i in 0 .. 1000;
let caller = whitelisted_caller();
}: {
for i in 0..i {
frame_system::Pallet::<T>::set_storage(
&[i as u8; 32],
vec![i as u8; 1024],
);
}
}
// 基准测试:杀死存储项
kill_storage {
let i in 0 .. 1000;
let caller = whitelisted_caller();
for i in 0..i {
frame_system::Pallet::<T>::set_storage(
&[i as u8; 32],
vec![i as u8; 1024],
);
}
}: {
for i in 0..i {
frame_system::Pallet::<T>::kill_storage(&[i as u8; 32]);
}
}
// 基准测试:杀死前缀
kill_prefix {
let p in 0 .. 1000;
let caller = whitelisted_caller();
for i in 0..p {
frame_system::Pallet::<T>::set_storage(
&[i as u8; 32],
vec![i as u8; 1024],
);
}
}: {
frame_system::Pallet::<T>::kill_prefix(&[0u8; 16], None);
}
// 基准测试:备注
remark {
let b in 0 .. 1024*1024;
let caller = whitelisted_caller();
let remark_message = vec![0u8; b as usize];
}: _(RawOrigin::Signed(caller), remark_message)
// 基准测试:备注带事件
remark_with_event {
let b in 0 .. 1024*1024;
let caller = whitelisted_caller();
let remark_message = vec![0u8; b as usize];
}: _(RawOrigin::Signed(caller), remark_message)
}
// 完整示例:Substrate 系统模块性能基准测试配置
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller};
use frame_support::traits::UnfilteredDispatchable;
use frame_system::{Call, Config, Pallet, RawOrigin};
use sp_runtime::traits::Hash;
#[benchmarks]
mod benchmarks {
use super::*;
// 设置代码哈希基准测试
#[benchmark]
fn set_code() {
let caller = whitelisted_caller();
let code = vec![0u8; 1024];
let hash = T::Hashing::hash(&code);
#[extrinsic_call]
_(RawOrigin::Signed(caller), code);
assert!(Pallet::<T>::code_hash().is_some());
}
// 设置存储项基准测试
#[benchmark]
fn set_storage(x: Linear<0, 1024>) {
let caller = whitelisted_caller();
let key = vec![0u8; 32];
let value = vec![0u8; x as usize];
#[block]
{
Pallet::<T>::set_storage(&key, value);
}
}
// 杀死存储项基准测试
#[benchmark]
fn kill_storage(x: Linear<0, 1024>) {
let caller = whitelisted_caller();
let keys: Vec<_> = (0..x).map(|i| vec![i as u8; 32]).collect();
// 预先设置存储
for key in &keys {
Pallet::<T>::set_storage(key, vec![0u8; 64]);
}
#[block]
{
for key in keys {
Pallet::<T>::kill_storage(&key);
}
}
}
// 备注操作基准测试
#[benchmark]
fn remark(x: Linear<0, 4096>) {
let caller = whitelisted_caller();
let remark = vec![0u8; x as usize];
#[extrinsic_call]
_(RawOrigin::Signed(caller), remark.clone());
}
// 备注带事件基准测试
#[benchmark]
fn remark_with_event(x: Linear<0, 4096>) {
let caller = whitelisted_caller();
let remark = vec![0u8; x as usize];
#[extrinsic_call]
_(RawOrigin::Signed(caller), remark.clone());
}
// 验证基准测试函数
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
crate::mock::TestRuntime
);
}
// 模拟测试环境配置
#[cfg(test)]
mod mock {
use frame_support::{
construct_runtime, parameter_types,
traits::{ConstU32, ConstU64},
};
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;
type Block = frame_system::mocking::MockBlock<TestRuntime>;
construct_runtime!(
pub enum TestRuntime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system,
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const SS58Prefix: u8 = 42;
}
impl frame_system::Config for TestRuntime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
// 创建测试环境
pub fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default().build_storage::<TestRuntime>().unwrap();
t.into()
}
}
// 基准测试主函数
fn main() {
// 运行基准测试
frame_benchmarking::Benchmarking::run::<mock::TestRuntime>(
&std::path::PathBuf::from("benchmark_results"),
);
}
1 回复
FRAME插件frame-system-benchmarking使用指南
概述
frame-system-benchmarking是Substrate框架中用于系统模块性能基准测试的核心工具。它允许开发者对FRAME模块进行精确的性能测量,确保区块链网络在各种负载条件下的稳定运行。
主要功能
- 测量交易执行时间
- 评估存储操作性能
- 测试内存使用情况
- 生成权重参数
安装与配置
添加依赖
[dependencies]
frame-benchmarking = { version = "4.0.0", default-features = false }
基准测试结构
#[benchmarks]
mod benchmarks {
use super::*;
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_system::RawOrigin;
#[benchmark]
fn do_something() {
let caller = whitelisted_caller();
#[extrinsic_call]
_(RawOrigin::Signed(caller));
assert!(Something::get().is_some());
}
}
使用示例
基本基准测试
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn transfer() {
let from: T::AccountId = whitelisted_caller();
let to: T::AccountId = account("to", 0, 0);
let value = T::Balance::from(100u32);
#[block]
{
Pallet::<T>::transfer(
RawOrigin::Signed(from.clone()).into(),
to.clone(),
value,
)?;
}
}
}
复杂操作基准测试
#[benchmark]
fn batch_transfer(n: Linear<1, 1000>) {
let caller = whitelisted_caller();
let recipients = (0..n).map(|i| account("recipient", i, 0)).collect::<Vec<_>>();
let value = T::Balance::from(10u32);
#[block]
{
for recipient in recipients {
Pallet::<T>::transfer(
RawOrigin::Signed(caller.clone()).into(),
recipient,
value,
)?;
}
}
}
运行基准测试
命令行执行
cargo run --release --features runtime-benchmarks -- benchmark pallet \
--chain=dev \
--pallet=pallet_your_module \
--extrinsic="*" \
--steps=50 \
--repeat=20 \
--template=./frame-weight-template.hbs \
--output=./weights.rs
权重生成
基准测试完成后会自动生成权重文件:
impl<T: Config> WeightInfo for Pallet<T> {
fn transfer() -> Weight {
Weight::from_parts(100_000_000, 0)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
最佳实践
- 测试覆盖:确保测试所有重要的执行路径
- 参数范围:使用适当的参数范围(Linear参数)
- 环境配置:在release模式下运行测试
- 结果验证:验证基准测试结果的合理性
注意事项
- 基准测试应在隔离环境中进行
- 考虑网络延迟和硬件差异
- 定期更新基准测试以适应代码变化
- 使用真实的交易数据进行测试
通过正确使用frame-system-benchmarking,开发者可以确保其Substrate模块在各种条件下都能保持最佳性能。
完整示例demo
//! 完整的FRAME模块基准测试示例
use frame_benchmarking::{benchmarks, whitelisted_caller, account};
use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::RawOrigin;
use sp_runtime::traits::StaticLookup;
// 假设的Pallet配置
pub trait Config: frame_system::Config {
type Currency: Currency<Self::AccountId>;
}
// 基准测试模块
#[benchmarks]
mod benchmarks {
use super::*;
use crate::Pallet;
// 基础转账基准测试
#[benchmark]
fn transfer() -> DispatchResult {
// 创建白名单调用者账户
let from: T::AccountId = whitelisted_caller();
// 创建接收者账户
let to: T::AccountId = account("to", 0, 0);
// 设置转账金额
let value = T::Currency::balance_from(100u32.into());
// 初始资金设置
T::Currency::make_free_balance_be(&from, T::Currency::balance_from(1000u32.into()));
#[block]
{
// 执行转账操作
Pallet::<T>::transfer(
RawOrigin::Signed(from.clone()).into(),
to.clone().into(),
value,
)?;
}
Ok(())
}
// 批量转账基准测试
#[benchmark]
fn batch_transfer(n: Linear<1, 100>) -> DispatchResult {
// 创建白名单调用者账户
let caller = whitelisted_caller();
// 创建多个接收者账户
let recipients = (0..n)
.map(|i| account("recipient", i, 0))
.collect::<Vec<T::AccountId>>();
// 设置每笔转账金额
let value = T::Currency::balance_from(10u32.into());
// 初始资金设置
let total_amount = value.saturating_mul(n.into());
T::Currency::make_free_balance_be(&caller, total_amount.saturating_mul(2u32.into()));
#[block]
{
// 执行批量转账
for recipient in recipients {
Pallet::<T>::transfer(
RawOrigin::Signed(caller.clone()).into(),
recipient.into(),
value,
)?;
}
}
Ok(())
}
// 复杂业务逻辑基准测试
#[benchmark]
fn complex_operation(x: Linear<1, 10>, y: Linear<1, 10>) -> DispatchResult {
let caller = whitelisted_caller();
#[block]
{
// 模拟复杂操作,包含多个存储读写
for i in 0..x {
for j in 0..y {
// 执行一些存储操作
SomeStorage::<T>::insert((i, j), i * j);
}
}
// 执行一些计算密集型操作
let mut result = 0u64;
for k in 0..1000 {
result = result.wrapping_add(k);
}
// 最终结果存储
FinalResult::<T>::put(result);
}
Ok(())
}
}
// 实现WeightInfo trait
impl<T: Config> WeightInfo for Pallet<T> {
fn transfer() -> Weight {
Weight::from_parts(95_000_000, 0)
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(2))
}
fn batch_transfer(n: u32) -> Weight {
Weight::from_parts(120_000_000, 0)
.saturating_add(Weight::from_parts(n as u64 * 80_000_000, 0))
.saturating_add(T::DbWeight::get().reads(3 + n as u64))
.saturating_add(T::DbWeight::get().writes(2 + n as u64))
}
fn complex_operation(x: u32, y: u32) -> Weight {
Weight::from_parts(200_000_000, 0)
.saturating_add(Weight::from_parts((x * y) as u64 * 50_000_000, 0))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(3 + (x * y) as u64))
}
}
这个完整示例包含了:
- 基础转账操作的基准测试
- 批量转账操作的基准测试,使用Linear参数控制批量大小
- 复杂业务逻辑的基准测试,包含嵌套循环和多个存储操作
- 完整的WeightInfo实现,为每个基准测试函数提供准确的权重计算
- 适当的错误处理和资源管理
每个基准测试都包含了详细的注释说明,展示了如何正确使用frame-system-benchmarking的各种功能特性。