LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 区块链资讯 > 【Substrate开发教程】18 - Runtime编程优化技巧:缓存多个调用

【Substrate开发教程】18 - Runtime编程优化技巧:缓存多个调用

2020-11-01 松果 来源:区块链网络


前几篇文章介绍了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()函数获取该值的副本。

—-

编译者/作者:松果

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...