中如何计算工龄_在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,一经查实,立即删除!

相关文章

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

安卓文件共享云服务器 内容精选换一换华为云帮助中心&#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…

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

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

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

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

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

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

OSG+VS2013+Win7 环境搭建,osgvs2013

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

像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…

a5d27 emmc启动 修改1

a5d27第1级bootloader是从sdhc0(emmc)加载还是从sdhc1(sd卡)加载&#xff0c; 只需要修改board/sama5d2_xplained文件即可 修改CONFIG_SDHC* y 这个宏定义在board/sama5d2_xplained.c中的void at91_sdhc_hw_init(void)函数实现 从上面代码可以看出第1级的bootloader只支持一…

矩阵每一行重复_【剑指offer】65 矩阵中的路径

- 题目描述请设计一个函数&#xff0c;用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始&#xff0c;每一步可以在矩阵中向左&#xff0c;向右&#xff0c;向上&#xff0c;向下移动一个格子。如果一条路径经过了矩阵中的某一…

mysql5.464位下载_MySQL Front 64位

MySQL-Front是一款实用的MYSQL数据库管理工具&#xff0c;软件自带了简体中文语言&#xff0c;与mysql数据库连接后就可以对其地蚝各类管理操作了&#xff0c;比如对域进行编辑、增加和删除&#xff0c;执行sql脚本或者导出数据库等操作&#xff0c;除此之外还可以将数据库保存…

a5d27 第1级bootloader启动问题

drivers/sdhc.c里的 static int sdhc_set_clock(struct sd_card *sdcard, unsigned int clock) 这段代码总是会超时&#xff0c;造成启动失败。 log如下图

LDA-Latent Dirichlet Allocation 学习笔记

以下内容主要基于《Latent Dirichlet Allocation》,JMLR-2003一文&#xff0c;另加入了一些自己的理解,刚开始了解&#xff0c;有不对的还请各位指正。 LDA-Latent Dirichlet Allocation JMLR-2003 摘要&#xff1a;本文讨论的LDA是对于离散数据集&#xff0c;如文本集&#xf…

app 图标规格参考表

转自&#xff1a;http://www.cocoachina.com/appstore/top/2012/1105/5031.html 像我一样记不住iOS应用图标像素尺寸的开发者不在少数&#xff0c;我经常需要查询不同设备上的应用尺寸&#xff0c;为了方便自己、方便大家&#xff0c;我制作了下面的图表供大家参考。 iPhone、i…

mysql数据库开启远程连接_安装MySQL数据库并开启远程访问

一、安装MySQL数据库MySQL安装在系统盘下(C:\Program Files)&#xff0c;方便系统备份。1.双击安装程序&#xff0c;勾选“I accept the license terms”&#xff0c;点击“Next”按钮。2.检测需要的安装&#xff0c;直接点击Next。3.按照下图所示步骤安装。4.设置密码&#xf…

flume mysql hdfs_利用Flume将MySQL表数据准实时抽取到HDFS

一、为什么要用到Flume在以前搭建HAWQ数据仓库实验环境时&#xff0c;我使用Sqoop抽取从MySQL数据库增量抽取数据到HDFS&#xff0c;然后用HAWQ的外部表进行访问。这种方式只需要很少量的配置即可完成数据抽取任务&#xff0c;但缺点同样明显&#xff0c;那就是实时性。Sqoop使…