查看原文
其他

新的 WebAssembly 仪器

DFINITY Internet Computer 2024-01-29



背景
互联网计算机(IC)是一个基于区块链的平台,托管通用应用程序,每个应用程序都在 WebAssembly(Wasm)虚拟机中运行,以确保安全性、性能和确定性。


IC 上的应用程序生命周期包括以下步骤:
  • 发展,使用受支持的高级语言之一(Rust、Motoko、Typescript、Python…)和相应的容器开发套件库(CDK),开发人员可以实现其通用应用程序。

  • Wasm 编译和部署,应用程序源代码被编译并与 CDK 库链接,生成的 Wasm 二进制文件部署在 IC 上,称为容器。

部署后,容器就准备好接收来自用户和其他容器的消息,在执行第一个消息之前,新部署的 Wasm 二进制文件必须再经历两次转换:
  • 仪器,互联网计算机将系统级代码插入到 Wasm 二进制文件中以计算执行指令的数量。

  • 原生编译,最后,经过检测的 Wasm 二进制文件被编译为本机 x86 代码,以实现接近本机的应用程序性能。

更多详细信息可以在互联网计算机上的 WebAssembly 文章中找到。‍‍
现在,让我们深入了解仪器流程。


互联网计算机通过注入微小的代码片段来计算执行指令的数量,从而对 Wasm 二进制文件进行检测,这是为了确保容器执行终止并且容器公平充值所必需的,执行指令的数量在其他区块链上也称为程序计数器。
由于必须正确计算每条执行的指令,因此检测步骤对于容器的性能和互联网计算机块速率的一致性至关重要。
旧的 Wasm 检测算法运行良好,但对于某些类型的应用程序(例如语言解释器)效率低下,新的 Wasm 仪器解决了这些低效率问题,并为此类应用程序实现了一个数量级的更好性能,使开发人员能够更高效、更便宜地运行他们的容器。
旧的仪表算法
旧检测算法的高级思想是检测“直线代码”(基本块)并注入一些 Wasm 指令以更新每个基本块开头的指令计数器,对于可重入块,例如函数和循环,该算法还添加了对指令外条件的检查。
旧的仪器算法是在互联网计算机创世之前编写的,因为当时没有其他方法来测量燃料 / Gas。
问题案例
问题一:高估
旧的检测算法无法正确处理控制流跨多个块传输的情况,该算法高估了执行指令的数量:
原始 Wasm 代码


仪表化 Wasm 代码


在上面的示例中,br 指令将控制转移到 $b1 块的末尾,跳过 N 条指令,然而,该算法将 N 条指令视为 $b1 块的一部分,并在块的开头考虑它们,许多语言使用嵌套块和标记分支来实现 switch/match,这意味着高估在实践中可能很常见。
问题的根本原因是算法错误地检测到了基本块,“直线代码”的关键特性是整个块必须无条件执行,如上例所示,该属性不成立。
问题二:性能
基本块具有单个入口和单个出口点,有一个名为 block 的 Wasm 指令,但该指令既不标记“直线代码”的开始,也不标记“直线代码”的结束,旧算法错误地将 block 指令视为基本块的开始。
由于 instruction_counter 存储在全局变量中,因此更新它需要两次内存访问:一次加载和一次存储,旧的仪器错误地将它们插入到每个 block Wasm 指令之后,这对一些具有大量嵌套 block 的应用程序产生了巨大的负面影响。
问题三:调度和公平性
尽管旧的工具将所有指令在成本方面视为相同,但指令的实际成本取决于其类型,例如,除法比加法更昂贵,因此,即使 Wasm 指令数量相同,对除法收取更多费用也是公平的。
此外,对所有指令均等收费对互联网计算机调度程序提出了巨大的挑战,为了具有确定性,它尝试在每轮中安排相同数量的指令,如果大多数指令恰好是“轻”的,则回合会提前完成,而如果大多数指令是“重”的,用户可能会观察到意外的延迟。
新的仪表算法
为了解决问题 1(高估)和问题 2(性能),提出了一种新的仪表算法,用于通过 NNS 进行投票,它正确检测基本块(“直线代码”)并减少内存访问次数。
更具体地说,新的检测算法的工作原理如下:
  • 注入一个新的全局变量:instruction_counter = slice_limit

计数器被初始化为执行片限制并在执行期间减少。
  • 在每个可重入块(函数入口或循环指令)的开头,注入以下代码片段:instruction_counter = instruction_counter - N if instruction_counter < 0 call out_of_instructions()

  • 将递减该块中顶级指令 instruction_counter 的数量(未嵌套在其他块中),直到下一个块开始。

  • 如果计数器低于零,则调用 out_of_instructions() 处理程序来暂停(使用 DTS)或终止执行。

  • 在每个基本块的开头(在每个 if, else, br, br_if, br_table, end, return, unreachable, return_call, return_call_indirect 指令之后),注入:instruction_counter = instruction_counter - N

  • 将递减该块中顶级指令 instruction_counter 的数量(未嵌套在其他块中),直到下一个块开始。

让我们快速比较一下新旧仪器算法,这是之前看到的示例:
旧 Wasm 仪器


新 Wasm 仪器


由于块指令既不是新算法中“精简代码”的开始也不是结束,因此每个块指令之后不再有递减,因此本示例的内存访问次数仅减半,这看起来似乎是一个微妙的差异,但许多语言解释器在解释循环的核心使用相当复杂的匹配/切换语句。
更多详细信息可以在 instrument() 的函数源代码中找到:
  • github.com/dfinity/ic

成本调整
每条 Wasm 指令都被分配一个“权重”,这是区块链世界的标准做法,不同的指令通常具有不同的成本,例如,EVM 中每个操作码的 Gas 成本各不相同,如黄皮书中所述。
因此,为了解决问题 3(出块率和公平性),每个 Wasm 指令在重新设计仪器时都被分配了一个新的“权重”。
这会导致指令成本模型不一致,并可能影响某些工作负载的总 Cycles 消耗,为了解决这个问题,我们在向社区传达变更、与 DFINITY 团队内部合作以及测试方面投入了大量精力。
为了给每个 Wasm 指令分配正确的权重,大多数指令都经过了基准测试,基准测试可以很好地估计 CPU 在每条 Wasm 指令上花费的实际时间。
更多详细信息可以在 wasm_instructions_bench() 的函数中找到:
  • github.com/dfinity/ic

基准测试的结果与 CPU 指令表密切相关,这证实了基准测试是有意义的,并且不会给开发人员带来意外,成本调整的简短摘要:


更多详细信息可以在 instruction_to_cost_new() 的函数中找到:
  • github.com/dfinity/ic

结果
您可以在下面了解新的检测算法和新的 Wasm 指令权重如何影响某些应用程序,虽然这不是详尽的列表,但它表明容器开发人员应该对不同类型的应用程序有什么期望。


请注意,指令数量增加 X% 并不直接意味着总体成本增加 X%,因为这不包括入口费用和消息执行费用。
总体而言,无论是在执行指令的数量还是执行时间方面,解释器的效率都提高了一个数量级,这为互联网计算机上的新语言(如 TypeScript、JavaScript 和 Python)打开了大门。
对于其余的工作负载,结果在旧检测结果周围波动 +/-20%,这是一个适度的开销,开发人员不应感到惊讶。
新的工具已在所有主网子网和开发环境(dfx v0.15+)上实施和推出。
参考
文档 - ICP 上的 WebAssembly
  • internetcomputer.org/capabilities/webassembly

博客文章

ICP 开发者论坛 - 新型 Wasm 仪器
  • forum.dfinity.org/t/new-wasm-instrumentation/22080

如果您有疑问/建议或只是想与互联网计算机开发人员和 DFINITY 工程师见面,请加入:
  • forum.dfinity.org

了解有关互联网计算机的更多信息:



来源:DFINITY翻译:Catherine

-              -


互联网计算机从零到 Dapp 教育系列总结

互联网计算机区块链迈出了欧洲数字主权的第一步

异步友好的性能计数器





你关心的 IC 内容

技术进展 | 项目信息 | 全球活动



长按关注 IC 微信公众号

掌握最新资讯


*添加小助手微信 comiocn 进交流社群


继续滑动看下一个

新的 WebAssembly 仪器

DFINITY Internet Computer
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存