利用BAL和并行执行实现 10GigaGas/s EVM 执行

本文为机器翻译
展示原文

作者: Po周琦

特别感谢ToniDragan 的反馈和评论!

抽象的

以太坊正在通过逐步提高The Blockgas 上限来扩容 L1 缓存。然而,大幅提高 gas 上限(例如Dankrad 提出的 100 倍提升方案)会迅速触及磁盘 I/O 和 CPU 执行速度的瓶颈。预热和EIP-7928 区块级访问控制列表 (BAL)可以消除大部分 I/O 读取阻塞,将主要瓶颈转移到执行本身。与此同时,现有客户端仍然按顺序执行交易,从根本上限制了吞吐量。

BAL ( 我们团队两年前也探索过这个想法)实现了完美的并行执行,但其性能上限仍不明确。为了解决这个问题,我们构建了一个纯执行环境,其中包含:

  • 预加载状态,模拟相关账户、存储槽位和合约代码通过BAL提示预先解析的环境;
  • 预先恢复的发送方,利用大多数客户端中已实现的并行发送方恢复功能;
  • 省略状态根计算,其成本可以分摊到更大的块中。

在此环境下,我们使用BAL对每事务并行执行进行了基准测试。结果表明,在现代 16 核商用 PC 上,纯执行吞吐量超过10 GigaGas/s ,而当前 Reth 客户端在相同条件下仅能达到约 1.2 GigaGas/s。这表明,一旦上述瓶颈得到彻底解决,EVM 执行能力可以比当前客户端基准性能提升一个数量级。


我们今天所处的位置

以太坊在 Fusaka 升级中将 gas 上限从 4500 万提升至 6000 万。假设 gas 上限提升 100 倍,则生成的区块将包含约 4.5 吉 gas。为了将验证时间控制在三秒以内,验证者至少需要 1.5 吉 Gas/s 的执行吞吐量。然而, Base 的公开基准测试表明,在普通硬件上运行的现代客户端最高只能达到约 600 吉 Gas/s。这一限制主要源于顺序执行:尽管多核 CPU 已经可用,但现有客户端仍然采用串行方式处理交易,导致大多数核心未得到充分利用。

发射有效载荷Geth MGas/s Reth MGas/s
基础主网模拟316.4 591.6

当前性能(~0.6 GGas/s)与 100 倍扩展所需的性能(~1.5 GGas/s)之间的差距仍然很大——这促使我们努力实现完全并行的 EVM 执行。


我们是如何做到的

为了研究BAL带来的极致并行执行性能,我们构建了一个纯执行环境,移除了所有无关的非执行部分,从而能够测量 BAL 并行性的真正上限。利用 Rust 的无垃圾回收设计、对多线程调度的细粒度控制以及 Reth 的高性能,我们修改了 Reth 客户端,并使用 revm 作为 EVM 执行引擎进行本次实验。

纯粹执行仿真的简化

  • 整个链状态预先加载到内存中(因为我们可以根据 BAL 的读取位置进行批量 I/O)。
  • 所有交易都已恢复发送方身份(发送方恢复可以事先完全并行化)。
  • 执行后不会进行状态根计算和数据库提交(这是一个瓶颈,但不是本研究的主要重点)。

工程工作及搭建

  • 修改了 Reth 客户端,使其支持转储完整的执行依赖项,包括块、BAL、最后 256 个块哈希以及从BAL读取集提示解析的块前状态。
  • 为 Revm 添加了一个适配器,用于加载blockEnvstatetxEnv ,并为每个交易创建一个单独的 EVM 实例。
  • 并行粒度 =按事务
  • 硬件:AMD Ryzen 9 5950X(16 核),128 GB 内存。
  • 数据集:2000 个主网区块( #23600500–23602500 )。
  • 指标:每秒 Gas 消耗量 = 总 Gas 消耗量 / 纯执行模拟时间。

基准测试套件可在此处获取:
:backhand_index_pointing_right: https://github.com/dajuguan/evm-benchmark


结果

我们的评估首先针对revm的顺序执行性能进行了校准,然后逐步引入并行执行。并行扩展性分析表明,运行时间最长的交易的延迟是限制整体加速的关键路径。为了缓解这一限制,我们模拟了更高的区块gas限制,从而显著提升了BAL的并行性。在16个线程和1G区块gas限制下,纯执行吞吐量达到了约14 GGas/s

基线对齐与顺序执行

我们首先尝试复现 Reth 的基准测试结果。在预加载 KZG 设置的情况下,对主网数据进行顺序运行,纯执行速度达到了 1,212 MGas/s。

这一连续结果将作为我们后续所有实验的参考点。


并行执行及其关键路径瓶颈

为了评估实际加速效果以及阿姆达尔定律对事务级并行性的影响,我们进行了逐事务并行执行实验,以量化运行时间最长的事务对可实现加速效果的影响。

详细结果如下所示(其中“最长交易延迟”是指每个区块中运行时间最长的交易的总执行时间):

线程吞吐量(百万燃气/秒)最长传输延迟总时间
1 1258 6.06秒33.47秒
2 2460 6.04秒17.12秒
4 3753 6.10秒10.71秒
8 4824 6.00秒8.73秒
16 5084 6.04秒8.29秒

总体而言,扩展性测试结果与阿姆达尔定律高度吻合:虽然吞吐量随着线程数的增加而提高,但块执行时间受限于最长事务,在16个线程的情况下,最长事务约占总执行时间的70%,这使得可实现的加速比上限约为5倍,而非16核机器的理想16倍。这表明,可扩展性取决于每个块的关键路径,而非原始计算能力。

可以通过降低最长交易的主导地位来缓解这种关键路径限制,例如通过EIP-7825:交易 gas 限制上限或增加区块 gas 限制——本文探讨的方法。


7928 + 巨型模块 = 大规模并行

由于每个区块的关键路径限制了并发性,我们尝试使用更高 gas 费用的“巨型区块”来提高并行性。为了模拟这种情况,我们并行执行了多个连续主网区块(即巨型区块或批次)的交易,并在批次中的所有交易完成后才提交状态(实验中为空操作)。这有效地将多个区块聚合为一个大型执行单元。

超大规模工作负载下的并行性分析

我们首先评估了 50 个代码块,模拟了不同线程数下平均代码块 gas 消耗量为 1,053 M 的情况。完整结果如下所示:

线程吞吐量(百万燃气/秒)最长传输延迟总时间
1 1,440 0.50秒29.26秒
2 2,793 0.50秒15.08秒
4 5,167 0.52秒8.15秒
8 9,095 0.54秒4.63秒
16 14,001 0.59秒3.01秒

由于区块尺寸如此之大,运行时间最长的交易不再主导关键路径——在 16 个线程下,它们仅占总执行时间的不到 20%。吞吐量几乎与线程数呈线性关系:使用 16 个线程,我们实现了 14 GGas/s 的吞吐量,比顺序执行快了大约 10 倍,接近理想的线性扩展。这令人非常鼓舞。在我们的实验中,唯一剩下的主要关键路径是point_evaluation预编译,而这部分难以轻易并行化。

不同模块气体利用率下的吞吐量

为了评估并行执行如何随着区块 gas 使用量的增加而扩展,我们执行了连续区块批次,同时改变了The Block批次大小(即分组到一个大型区块中的区块数量),从而模拟了不同的有效区块 gas 使用量。

线程块批次大小平均块状气体(M)吞吐量(百万燃气/秒)
16 1 21 5,084
16 2 42 6,641
16 5 105 8,814
16 10 210 10,228
16 25 526 12,152
16 50 1,053 14,001
16 100 2,106 14,887
16 200 4,212 15,298

随着The Block气使用量的增加,吞吐量持续提升,但并行度的增量提升却从每增加一倍块气所对应的约 30% 下降到约 3%。一旦批处理大小超过约 50 个块(约 10.53 亿块气),进一步增加块气带来的吞吐量提升微乎其微。


前景

我们的实验表明,将EIP-7928 与巨型区块相结合,能够显著提升交易执行的可扩展性,在现代 16 核通用处理器上实现了14 GigaGas/s 的纯执行吞吐量。然而,仍有几个问题尚未解决:

1. 发件人恢复

我们在纯执行基准测试中排除了发送方恢复。在我们的实验中,启用发送方恢复会使吞吐量降低约 2/3,在巨型块配置(10.53 亿块 gas)下降至约 5 GigaGas/s。

可能的缓解措施:GPU加速的发送方恢复。

2. 天然气定价模型

7702 交易的point_evaluation预编译和发送方恢复功能效率较低,其 gas 消耗效率也较低。在 EIP-7928 时代,可能需要重新评估其 gas 定价机制。

3. 交易 Gas 限额

提高区块 gas 限制可能需要保留当前的交易 gas 限制上限,以维持高并行性。

4. 加快BAL建设

构建器性能预计将成为主要瓶颈。提高BAL构建效率对于跟上纯执行吞吐量至关重要。

5. 优化状态提交

状态提交是另一个主要瓶颈。加快状态根计算速度和优化 trie 树提交对于维持高吞吐量执行至关重要。

其他作品

我们也探索了不同的任务调度策略,例如,根据 gas 消耗量或 gas 上限对高 gas 交易进行排序以提升优先级,以及简单的有序列表调度器 (OLS),其中交易保持自然区块顺序,并将每个新交易分配给第一个可用的核心。然而,当应用于主网数据时,对高 gas 交易进行优先级排序仅带来了微小的性能提升,并未显著影响整体吞吐量。

不同调度策略下的吞吐量

为了评估对整体吞吐量的影响,我们将优先调度高 gas 交易(按 gas 使用量或 gas 限制)与 OLS 进行了比较。

  • 普通块体上的结果:
线程(调度器)吞吐量(百万燃气/秒)最长传输延迟总时间
2(用气量) 2,726 5.70秒15.45秒
2(气体限制) 2,728 5.68秒15.44秒
2(OLS) 2,460 6.04秒17.12秒
4(用气量) 4,401 6.09秒9.57秒
4(气体限制) 4,321 6.18秒9.75秒
4(OLS) 3,753 6.10秒10.71秒
8(用气量) 5,455 6.15秒7.72秒
8(气体限制) 5,426 6.13秒7.76秒
8(OLS) 4,824 6.00秒8.73秒
16(用气量) 5,643 6.03秒7.47秒
16(气体限制) 5,531 6.05秒7.62秒
16(OLS) 5,084 6.04秒8.28秒
  • 平均区块气体量为 1053M 的巨型区块的测试结果:
线程(调度器)吞吐量(百万燃气/秒)最长传输延迟总时间
2(气体限制) 2,732 0.53秒15.42秒
2(OLS) 2,793 0.50秒15.08秒
4(气体限制) 5,114 0.54秒8.24秒
4(OLS) 5,167 0.52秒8.15秒
8(气体限制) 9,082 0.57秒4.64秒
8(OLS) 9,095 0.54秒4.63秒
16(气体限制) 14,181 0.63秒2.97秒
16(OLS) 14,001 0.59秒3.01秒

Toni 的分析表明,在最坏的情况下,优先处理高 gas 消耗的交易可以比 OLS 算法提升 20% 到 80% 的性能。然而,在实际应用中,使用真实的主网数据(代表平均情况)时,性能提升仅为 10% 左右,而且按 gas 限制、gas 使用量或 OLS 进行调度之间的差异微乎其微。在大型区块上,OLS 算法的性能几乎与基于 gas 限制的调度算法完全相同。这些观察结果表明,交易调度并非主要瓶颈;相反,主网交易的固有分布才是关键路径。


来源
免责声明:以上内容仅为作者观点,不代表Followin的任何立场,不构成与Followin相关的任何投资建议。
喜欢
收藏
评论