中如何计算工龄_在Substrate中如何计算交易权重

74f5fcedeec95c0a8532573f9d7808ed.png
建议在阅读本文之前,先掌握关于Substrate中交易费用设计的基本概念。如果还没有了解的童鞋,请移步:
Kaichao:Substrate 区块链应用的交易费用设计​zhuanlan.zhihu.com
e8e1d053987e96c6784aa591d55b5850.png

读完Substrate区块链应用的交易费用设计的小伙伴,应该掌握:

  • 什么是权重(weight)
  • 交易费用包括:基本费用字节费用权重费用
  • 交易级别:NormalOperational
  • 如何自定义一个权重计算方法: #[weight = FunctionOf(...)]
  • 如何使用固定权重值,#[weight = SimpleDispatchInfo::FixNormal(X)]

这篇文章会详细介绍,如何合理地设计runtime中dispatchable function (用户可调用的方法)的weight,学习这篇文章,希望大家可以掌握:

  • 如何写出合格的weight document;
  • 如何使用benchmark帮助计算weight

0. 概要

作为一名runtime developer, 在weights和fees方面,我们应该:

  • 最小化runtime function的复杂度和占用的计算资源;
  • 精确地定义相关runtime function的权重(weight)

为了达到以上要求,我们应该:

  • 在写runtime时,尽量多参考和follow优秀的设计和写法
  • 在注释中尽量写清楚方法的时间复杂度
  • 计算这些方法在真实世界中的成本(benchmark),并把它和时间复杂度结合在一起思考

1. Follow Runtime Best Practices

https://github.com/paritytech/substrate

2. Weights注释

首先,我们应该为在runtime中的dispatchable方法完善关于weight的注释。这不仅可以帮助我们确定weight值的设定,也可以帮助我们更有效地优化代码。关于weight的注释结果以时间复杂度表示的结果展示,例如:

O(A + logA + BlogC)

2.1 注释要写什么

weight相关的注释应当要包含对runtime方法的执行成本有明显影响的部分。比如:

  • 存储相关的操作 (read, write, mutate, etc.)
  • Codec(Encode/Decode)相关操作(序列化/反序列化 vecs或者大的结构体)
  • search/sort等成本高的计算
  • 调用其他pallet中的方法
  • ...

2.2 举例分析

对下面一段代码进行weight注释时的分析:

// Join a group of members.
fn join(origin) {let who = ensure_signed(origin)?;let deposit = T::Deposit::get(); // configuration constantlet sorted_members: Vec<T::AccountId> = Self::members();ensure!(sorted_members.len() <= 100, "Membership Full");match sorted_members.binary_search(&who) {// User is not a member.Err(i) => {T::Currency::reserve(&who, deposit)?;members.insert(i, who.clone());<Members<T>>::put(sorted_members);Ok(())},// User is already a member, do nothing.Ok(_) => Ok(()),}Self::deposit_event(RawEvent::Joined(who));
}

Storage和Codec操作

访问存储是一个成本很高的操作,所以我们应当写好注释并优化。

每一个存储操作都应当结合相关的Codec复杂度,写好注释

比如,如果你需要从一个存储项中读取一个vec中的值,weight应该这样写:

- One storage read to get the member of this pallet: `O(M)`

在这个例子中,在存储中读取vec有一个codec复杂度O(M),因为要对member M 进行反序列化操作。

稍后在module中,可能还会把一个数据再写入存储中,这也应该要有对应的注释:

- One storage write to update the members of this pallet: `O(M)`

Search, Sort 以及其他昂贵的计算

如果在runtime中需要搜索或者排序的话,同样也需要标注相应的复杂度。比如,如果你在一个已经排序的list中执行搜索,binary_search 操作的时间复杂度为O(logM), 如果是一个未经排序的list的话,复杂度为O(M)。所以注释应当像下面这样写:

- Insert a new member into sorted list: O(logM)

调用其他pallet和trait

如果你调用其他FRAME pallet的方法,直接调用或者通过trait设置,需要记录调用的那个方法的复杂度。比如,如果你写的方法保留了一下余额(在Balances里)或者发送一个event(通过System pallet),你再注释里应该写上:

- One balance reserve operation: O(B)
- One event emitted: O(E)

最终的注释

把以上的操作注释结合在一起,我们就可以为一个方法写上完整的注释:

# <weight>
Key: M (len of members), B (reserve balance), E (event)
- One storage read to get the members of this pallet: `O(M)`.
- One balance reserve operation: O(B)
- Insert a new member into sorted list: O(logM).
- One storage write to update the members of this pallet: `O(M)`.
- One event emitted: O(E)Total Complexity: O(M + logM + B + E)
# </weight>
注意: 在为方法写weight注释时可能引入了不同的参数,记得吧每个参数都标记好。

如果仔细看上面的样例代码,就可以看到有两个操作的时间复杂度都是O(M)(存储读和写),但是整体的时间复杂度O并没有把这部分考虑进来。

所以我们没法区分有同样复杂度的两个方法,这意味着可以在这个方法里加入很多复杂度为O(M), O(logM)... 的操作,但是并不会对最后的复杂度标记做任何更改:

weight(M, B, E) = K_1 + K_2 * M + K_3 * logM + B + E

对这部分区别,我们通过on-chain tests来衡量。

3. 如何大致推断weight

总结:综合考虑时间复杂度和具体的操作,参考目前FRAME中一些标志性的extrinsic(比如transfer),大致确定当前方法的weight数量级

对weight有了更深的了解之后,在测试之前,我们可以给runtime方法先设定一个暂时的权重值。更多时候,我们的extrinsics大概率都是normal transactions,所以关于权重的声明大概率会是像下面这样:

#[weight = SimpleDispatchInfo::FixedNormal(YOUR_WEIGHT)]

在深入探讨更加细致的方法之前,我们可以简单地参考一下现有pallet中方法的weight,给大家一个简单的参考:

  • System: Remark - 没有任何逻辑。就用权重值允许的最小值标注。
/// Make some on-chain remark.   
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]   
fn remark(origin, _remark: Vec<u8>) {       ensure_signed(origin)?;   
}
  • Staking: Set Controller. - 一次固定复杂度的存储读+写 (500,000)
#[weight = SimpleDispatchInfo::FixedNormal(500_000)]   
fn set_payee(origin, payee: RewardDestination) {       let controller = ensure_signed(origin)?;       let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;  let stash = &ledger.stash;<Payee<T>>::insert(stash, payee);   
} 
  • Balances: Transfer. - 固定的时间复杂度 (1,000,000)
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]   
pub fn transfer(origin, dest: <T::Lookup as StaticLookup>::Source, #[compact] value: T::Balance
) {       let transactor = ensure_signed(origin)?;       let dest = T::Lookup::lookup(dest)?;       <Self as Currency<_>>::transfer(&transactor, &dest, value, ExistenceRequirement::AllowDeath)?;   
}
  • Elections: Present Winner - O(voters) 复杂度的计算加上一次写操作 (10,000,000)
#[weight = SimpleDispatchInfo::FixedNormal(10_000_000)]   
fn present_winner( ... ) {//--lots-of-code--   
}
  • 如果想要在一个执行方法A的extrinsic最终会执行方法B,那么会简单地在B的权重值的基础上加上10_000,作为passthrough weight。 比如sudo中的sudo方法,详见:

https://github.com/paritytech/substrate/blob/master/frame/sudo/src/lib.rs#L123

https://github.com/paritytech/substrate/pull/4946

4. 如何计算weight

给runtime中的某个方法定一个权重值,是一件有些主观的事情,思考的维度也有很多,这里重点从技术角度,介绍如何给runtime中的方法进行性能测试,可以为确定方法的权重值多一些事实参考。

一般来说,想要给一个方法一个确定的权重值,可以从以下几个角度考虑:

  • 如果一个区块里只包括这一个方法的extrinsic,你希望最多包括多少笔;
  • 如果以balances.transfer作为基准,那这个方法要用多少权重;
  • ...

从不同的侧重点触发,可能得到的结果也会稍有不同。这里只介绍最后一种思考方式,如果我们相信Substrate目前FRAME中的function weight是合理的话 。

4.1 benchmark

简单来说,benchmark就是测试在指定的上下文中,执行指定的runtime function 花费了多少时间(in nanosencond)。

benchmark介绍

Substrate中有关于benchmark的宏,benchmarks!,大家直接就可以使用。

使用benchmarks!的整体结构如下,

benchmarks! {
_ {}
scenarioA {}: functionA(...)
scenarioB {}: functionA(...)
scenarioC {}: functionC(...)
}

_, scenarioA, scenarioB, scenarioC这部分,被称为arms,可以简单地理解为性能测试中的场景/分支。可以针对一个runtime function构建多种场景,比如最好情况、一般情况、最坏情况等;而后面的functionA, functionB就是构建完场景(上下文)后具体执行的runtime function。如果该方法和arms重名的话,可以用_代替省略。

建议大家在性能测试时尽可能保守,即考虑最坏情况来确定weight

关于构建测试场景(上下文),大家可以参考substrate现有的做法,比如:

  • 输入参数可以构造成升序、降序、随机;
  • 尽可能执行function中尽可能多的operations,比如转转账时涉及到创建/清空地址;
  • ...

benchmark示例参考:

benchmark示例参考:

https://github.com/paritytech/substrate/blob/master/frame/balances/src/benchmarking.rs

https://github.com/paritytech/substrate/blob/master/frame/identity/src/benchmarking.rs

暴露benchmark runtime api

impl_runtime_apis中,给实现了benchmarking的pallet添加对应的benchmark接口;

impl_runtime_apis! {impl frame_benchmarking::Benchmark<Block> for Runtime {fn dispatch_benchmark(module: Vec<u8>,extrinsic: Vec<u8>,steps: Vec<u32>,repeat: u32,) -> Option<Vec<frame_benchmarking::BenchmarkResults>> {use frame_benchmarking::Benchmarking;match module.as_slice() {b"pallet-balances" | b"balances" => Balances::run_benchmark(extrinsic, steps, repeat).ok(),b"pallet-timestamp" | b"timestamp" => Timestamp::run_benchmark(extrinsic, steps, repeat).ok(),_ => None,}}}
}

benchmark CLI

最后一步,为了使substrate CLI可以使用类似substrate benchmark (或者node-template benchmark)这样的subcommand来执行指定pallet的性能测试,我们还需要为cli添加对应的subcommand以及暴露benchmark host functions。因为目前node-template不是默认支持CLI中使用benchmark,所以我们需要对node-template做一些必要的微调:

具体做法请参考:

https://github.com/hammewang/substrate-multisig/​github.com

之所以在substrate官方的node-template中没有集成benchmark,也是Parity考虑后的结果:

https://github.com/paritytech/substrate/pull/4875

benchmark CLI如何使用

以multisig-template举例:

./target/release/node-template benchmark --chain dev --pallet multisig --extrinsic create_multisig_wallet --execution wasm --repeat 2 --steps 10

这里会把测试结果写成一个csv,方便后续统计。补充说明一下常用参数:

  • --execution wasm: 在wasm环境中执行性能测试,这里支持的所有可能的参数:[Native, Wasm, Both, NativeElseWasm]
  • --pallet: 想要测试的pallet,这里可以填pallet-multisig或者multisig
  • --chain: 链运行的初始状态,比如dev, local,或者在chain_spec 自定义
  • --steps: 从默认(1)开始,执行的最多样本点数量
  • --repeat: 每个样本点重复次数

4.2 如何得到相对准确的weight

在执行完上述命令,会得到如下结果:

Pallet: "multisig", Extrinsic: "create_multisig_wallet", Steps: [10], Repeat: 2
u,extrinsic_time,storage_root_time
1,418000,39000
1,217000,29000
100,200000,28000
100,200000,29000
199,203000,29000
199,195000,27000
298,201000,28000
298,202000,28000
397,197000,28000
397,196000,29000
496,201000,28000
496,244000,28000
595,234000,31000
595,199000,28000
694,231000,32000
694,246000,29000
793,218000,31000
793,196000,28000
892,205000,29000
892,222000,29000
991,244000,32000
991,228000,31000

根据第一行head可以看到,第二列就是执行这个extrinsic,在指定执行环境中,所花费的时间。大家可以根据需要对其进行处理,比如取平均值。

然后,大家可以在自己的机器上,再执行一遍balances.transfer的benchmark(我们选择了balances.transfer的weight作为基准参考)。

然后把两个性能测试的结果做一下对比,大致就可以得到一个相对准确的weight值。

再次友情提醒:请大家注意性能测试时,为了准确,覆盖尽可能多的情况(最好、最坏、一般)

这里贴一下Substrate目前已经有的部分benchmark结果:

80a1115aaadef9bd3df3f0f66a7a6259.png

补充

目前Substrate中的benchmark仍然处于早期状态,后期很可能会出现breaking changes。本文写于f41677d0, 想直接使用的童鞋请认准依赖版本。

希望可以给大家提供到一些,关于weight设定的基本概念,以及可行但粗糙的实践方法。欢迎共同交流。

最新动态

github repo和substrate官方dev文档是更新最及时、也是知识结构最系统的地方,值得star和收藏:

https://github.com/paritytech/substrate​github.comhttps://substrate.dev/​substrate.dev

或者中文资料:

http://subdev.cn/​subdev.cn

参考资料

https://substrate.dev/docs/en/conceptual/runtime/weight

https://github.com/paritytech/substrate/pull/3157

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wince 6.0 pb

1)删除目录C:/Program Files/Microsoft Platform Builder/6.00下的EtkRemov.inf文件 2)修改系统时间到未来某个时间点 3)新建一个文本文档 4)重命名新建文本文档为EtkRemov.inf 5)改回系统时间

安卓文件共享服务器,安卓文件共享云服务器

安卓文件共享云服务器 内容精选换一换华为云帮助中心&#xff0c;为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档&#xff0c;帮助您快速上手使用华为云服务。内网环境下&#xff0c;Windows云服务器之间怎样实现文件夹共…

linux curl命令验证服务器断点续传支持

有个同事说&#xff0c;发现现在对外下载安装包的服务器不支持断点续传&#xff0c;我听了一阵纳闷&#xff0c;lighttpd server对于静态文件应该默认支持断点续传的&#xff0c;登机器查看lighttpd配置文件发现 对断点续传的支持被禁用了&#xff0c;lighttpd的说明里对该配置…

代码补全_AI加持,Kite增加智能代码补全功能:减少一半操作,实时补全

代码补全工具 Kite 近日更新了最新的版本&#xff0c;增加了名为「Intelligent Snippets」的新功能。这一功能可以帮助开发者更为智能和高效的补全 Python 代码中的函数命令了。机器之心报道&#xff0c;机器之心编辑部。Kite 是一个著名的 Python 代码补全工具&#xff0c;支持…

Linux workqueue工作原理

1. 什么是workqueue Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量&#xff0c;使得线程处理的事务能够并行化。workqueue是内核中实现简单而有效的机制&#xff0c;他显然…

微信获取token服务器处理,微信硬件平台(九) 自己的服务器从微信获取token并保存txt...

1安装crontab:yum install crontabs说明&#xff1a;service crond start //启动服务service crond stop //关闭服务service crond restart //重启服务service crond reload //重新载入配置查看crontab服务状态&#xff1a;service crond status手动启动crontab服务&#xff1a…

Tomcat项目部署过程中的问题

1.项目发布问题 将发布的war包放置在webapp下时&#xff0c;点击bin目录中的startup.bat时&#xff0c;出现黑框一闪&#xff0c; 采用cmd登录时出现 The JRE_HOME environment variable is not defined correctly 2.问题是环境变量没有配置好 下面进行系统的配置&#xff08;前…

centos8 默认nginx路径_CentOS 8系统安装Nginx Web服务器及常见配置过程

Nginx是一种快速、轻量级的Web服务器&#xff0c;优势是Nginx的配置文件非常简单易用。它是Apache Web服务器的绝佳替代品。在本文中&#xff0c;惠主机将简单介绍如何在CentOS 8上安装Nginx Web服务器和常见基本配置过程。安装NginxNginx在CentOS 8的官方软件包存储库中可用。…

思科模拟服务器怎么写文件,思科模拟服务器配置教程

思科模拟服务器配置教程 内容精选换一换部署游戏应用前&#xff0c;您需要准备硬件以及华为云的环境&#xff0c;主要包括以下内容&#xff1a;硬件环境&#xff1a;您需要准备一台带有显卡的Windows机器&#xff0c;硬盘至少20G&#xff0c;用于运行游戏客户端。若您不需要运行…

vs2013编译osg缺少mfc120d.lib

因为MFC MBCS DLL缺少导致的&#xff0c;微软的解释如下&#xff08;http://msdn.microsoft.com/en-us/library/dn251007.aspx&#xff09;&#xff1a; MFC libraries (DLLs) for multibyte character encoding (MBCS) are no longer included in Visual Studio, but are avai…

【转载】MongoDB 1000W级数据 Insert和Query和Delete性能测试

加索引测试&#xff1a; > db.users.getIndexes() [ { "name" : "_id_", "ns" : "test.users", "key" : { "_id" : …

名词解释说明用英语怎么说_“恍然大悟”用英语怎么说?

当我们对思考已久的事情&#xff0c;突然明白或醒悟时&#xff0c;可以说是“恍然大悟”了。那么问题来了&#xff0c;“恍然大悟”用英语该怎么说呢&#xff1f;① it occurs to me这是一个使用频率很高的表达&#xff0c;日常生活中经常会用到。在美剧《摩登家庭》中&#xf…

彩虹六号服务器显示,彩虹六号怎么看自己在什么服务器 | 手游网游页游攻略大全...

发布时间&#xff1a;2015-09-10在游戏中新手玩家要注意些什么呢?今天就为大家带来新手必看技巧汇总,一起来看看吧. 新手必看技巧大全: 1.开局遇到人别急着标记,尽量先找到目标位置 2.如果开局 ...标签&#xff1a;发布时间&#xff1a;2016-04-29彩虹六号:围攻 地图攻略 总统…

再次遇到谓词推入

explain plan for with aa as(select a.agmt_id,sum(c.acct_bal) as card_bal, --借记卡期末存款余额a.card_open_org,a.OPEN_DATE, -- 发卡日期 a.CARD_NEW_STATUS, -- 卡片状态 a.cust_magr, -- 客户经理号 a.cust_no, -- 客户号a.corp_orgfrom dwf.f_agt_cadb_book_…

OSG+VS2013+Win7 环境搭建,osgvs2013

最近在学习虚拟现实&#xff0c;要使用OpenSceneGraph&#xff08;OSG&#xff09;&#xff0c;但是网上找不到VS2013的搭建教程&#xff0c;好烦。。。 只能自己在vs2010的基础上&#xff0c;修改一些bug&#xff0c;最后成功搭建。 步骤一&#xff1a;材料准备 a) Osg源码 当…

ShardedJedisPool 中可用连接数的小bug

ShardedJedisPool中&#xff0c;returnBrokenResource() 及 returnResource() &#xff0c;为施放资源、关闭连接的方法&#xff0c;若重复调用&#xff0c;导致 _numActive 当前活动数一直递减&#xff0c;会出现负数的情况。 假如在一个方法中设置了三个jedis连接&#xff0c…

Linux设备树翻译计划

本文翻译自Device Tree Usage主页&#xff1a; http://devicetree.org/Device_Tree_Usage 此译文为本人原创&#xff0c;若要转载请注明&#xff01;Linux device tree的背景&#xff08;引用自宋宝华博客&#xff09;&#xff1a;Linus Torvalds在2011年3月17日的ARM Linux邮件…

像postman上传文件_90%的测试工程师是这样使用Postman做接口测试的……

postman介绍&测试准备postman介绍&#xff1a;postman是一个开源的接口测试工具&#xff0c;无论是做单个接口的测试还是整套测试脚本的拨测都非常方便。前期准备&#xff1a;测试前&#xff0c;需要安装好postman, 客户端版本跟插件版本都行&#xff0c;根据个人需要选择安…

浅析STL allocator

一般而言&#xff0c;我们习惯的 C 内存配置操作和释放操作是这样的&#xff1a; 1 class FOO{}; 2 FOO *pf new FOO; 3 delete pf; 我们看其中第二行和第三行&#xff0c;虽然都是只有一句&#xff0c;当是都完成了两个动作。但你 new 一个对象的时候两个动作是&#xff…

十六进制除法运算法则_苏教版数学七年级上册 微课视频 2.6 有理数的乘法与除法(1)...

第一章《数学与我们同行》视频讲解 同步练习2.1 《正数与负数》2.2 有理数与无理数2.3 数轴2.4 绝对值与相反数(1)2.4 绝对值与相反数(2)2.5 有理数的加法与减法(1)2.5 有理数的加法与减法(2)2.6 有理数的乘法与除法(1)七、有理数的乘除法1.有理数的乘法法则法则一&#xff1…