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))
    }
}

最佳实践

  1. 测试覆盖:确保测试所有重要的执行路径
  2. 参数范围:使用适当的参数范围(Linear参数)
  3. 环境配置:在release模式下运行测试
  4. 结果验证:验证基准测试结果的合理性

注意事项

  • 基准测试应在隔离环境中进行
  • 考虑网络延迟和硬件差异
  • 定期更新基准测试以适应代码变化
  • 使用真实的交易数据进行测试

通过正确使用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))
    }
}

这个完整示例包含了:

  1. 基础转账操作的基准测试
  2. 批量转账操作的基准测试,使用Linear参数控制批量大小
  3. 复杂业务逻辑的基准测试,包含嵌套循环和多个存储操作
  4. 完整的WeightInfo实现,为每个基准测试函数提供准确的权重计算
  5. 适当的错误处理和资源管理

每个基准测试都包含了详细的注释说明,展示了如何正确使用frame-system-benchmarking的各种功能特性。

回到顶部