前几篇文章介绍了Substrate runtime模板编程的一些基础知识,包括模板里的几个宏:decl_module! decl_storage! decl_event!的用法。 这篇文章介绍一个runtime编程中的优化技巧,缓存多个调用,避免二次调用产生的运行成本。 Copy Rust中有两个常见的Trait:Copy和Clone。 Copy全称是std::marker::Copy,如果一个类型实现了Copy trait,则任何时候都可以通过简单的内存拷贝实现该类型的复制,而不会产生任何问题。 基本数据类型如数字类型、bool类型、共享借用指针&,都是具有Copy trait的类型; 数组类型,如果它的元素是Copy类型,则该数组是Copy类型; 元组类型,如果它的每一个元素都是Copy类型,则该元组是Copy类型,自动实现; struct和enum,如果它的内部每个元素都是Copy类型,可手动实现Copy类型; Clone Clone全称是std::clone::Clone,clone函数一般用于“基于语义的复制”操作。 对于Box类型(简单的指向堆的指针),clone执行的“深拷贝”; 对于Rc类型(reference counting,引用计数),clone把引用计数值加1。 Copy类型和Clone类型 Substrate runtime编程中的Copy类型和Clone类型如下所示: decl_storage! { trait Store for Module<T: Trait> as StorageCache { //Copy类型 SomeCopyValue get(fn some_copy_value): u32; //Clone类型 KingMember get(fn king_member): T::AccountId; GroupMembers get(fn group_members): Vec<T::AccountId>; } } 调用runtime存储会产生相应的成本,开发人员应该尽量减少调用次数。 Copy类型 对于Copy类型,只需要简单地重用该值即可重用之前的存储调用,该值在重用时会自动复制。 示例代码: fn increase_value_no_cache(origin, some_val: u32) -> DispatchResult { let _ = ensure_signed(origin)?; let original_call = <SomeCopyValue>::get(); let some_calculation = original_call.checked_add(some_val).ok_or("addition overflowed1")?; let unnecessary_call = <SomeCopyValue>::get(); let another_calculation = some_calculation.checked_add(unnecessary_call).ok_or("addition overflowed2")?; <SomeCopyValue>::put(another_calculation); let now = <system::Module<T>>::block_number(); Self::deposit_event(RawEvent::InefficientValueChange(another_calculation, now)); Ok(()) } 这里调用了两次<SomeCopyValue>::get(),分别赋值给original_call、unnecessary_call。 第二次调用<SomeCopyValue>::get()产生的unnecessary_call变量是不必要的,直接使用original_call即可: fn increase_value_w_copy(origin, some_val: u32) -> DispatchResult { let _ = ensure_signed(origin)?; let original_call = <SomeCopyValue>::get(); let some_calculation = original_call.checked_add(some_val).ok_or("addition overflowed1")?; let another_calculation = some_calculation.checked_add(original_call).ok_or("addition overflowed2")?; <SomeCopyValue>::put(another_calculation); let now = <system::Module<T>>::block_number(); Self::deposit_event(RawEvent::BetterValueChange(another_calculation, now)); Ok(()) } Clone类型 如果类型是clone,而不是copy,使用clone()函数比二次调用runtime存储更好。 示例代码: fn swap_king_no_cache(origin) -> DispatchResult { let new_king = ensure_signed(origin)?; let existing_king = <KingMember<T>>::get(); ensure!(!Self::is_member(&existing_king), "current king is a member so maintains priority"); ensure!(Self::is_member(&new_king), "new king is not a member so doesn't get priority"); let old_king = <KingMember<T>>::get(); <KingMember<T>>::put(new_king.clone()); Self::deposit_event(RawEvent::InefficientKingSwap(old_king, new_king)); Ok(()) } 这里调用了两次<KingMember<T>>::get(),分别赋值给existing_king、old_king。 第二次调用<KingMember<T>>::get()是不必要的,使用existing_king.clone(): fn swap_king_with_cache(origin) -> DispatchResult { let new_king = ensure_signed(origin)?; let existing_king = <KingMember<T>>::get(); let old_king = existing_king.clone(); ensure!(!Self::is_member(&existing_king), "current king is a member so maintains priority"); ensure!(Self::is_member(&new_king), "new king is not a member so doesn't get priority"); <KingMember<T>>::put(new_king.clone()); Self::deposit_event(RawEvent::BetterKingSwap(old_king, new_king)); Ok(()) } 总结 在进行Substrate Runtime开发时,频繁创建临时变量会导致Runtime存储调用增加,从而增加运行成本,应该尽量减少不必要的二次调用。 对于Rust基本数据类型,即存储在栈上的可以直接拷贝值的数据类型(Copy类型),直接使用该值即可; 对于Rust复合数据类型或Substarte FRAME定义的数据类型,即存储在堆上的需要通过指针或引用方式调用的数据类型(Clone类型),使用该值的clone()函数获取该值的副本。 —- 编译者/作者:松果 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
【Substrate开发教程】18 - Runtime编程优化技巧:缓存多个调用
2020-11-01 松果 来源:区块链网络
LOADING...
相关阅读:
- KMEX合约委托类型---止盈止损说明2020-09-25
- 好牛资讯解读——DEX的类型2020-09-23
- ALOKEX数字货币合约种类与操作类型2020-09-15
- 5种类型的数字货币钱包及其优缺点2020-09-10
- 不同类型的加密货币交易所–尼日利亚人的最佳选择是什么?2020-08-09