跨客户端执行指标规范
原始提案文档及规范由@rjl493456442提供:标准化以太坊性能指标 - HackMD
这项功能最初由Gary 发起,他在 Geth 中实现了慢速区块日志记录。从那时起,我认为这项功能在很多方面都非常有用,如下所述,于是我与他合作,将其标准化并推进这一愿景的实现。
我认为这有助于重新定价、ScaleL1跟踪以及bloatnet(我目前关注的重点)。不过,我觉得我们能做的远不止这些。
1. 引言与动机
这项计划存在的意义
以太坊的多客户端理念是一项核心优势——但也带来了一个挑战:我们如何比较不同实现方式的性能?
标准化的执行指标通过以下方式解决了这个问题:
- 跨客户端性能比较——公平、同等条件下的基准测试
- 网络健康监控——在执行瓶颈影响共识之前识别它们。
- 数据驱动的协议研究——利用真实执行数据验证EIP提案
- 异常检测——检测异常区块(高 gas 消耗、复杂的状态访问模式)
实际案例:EIP-7907 分析
EIP-7907 分析恰恰说明了为什么标准化的执行指标至关重要。
我使用 Geth 指标测量了以下内容:
- 代码读取延迟:107毫秒至904毫秒,具体取决于字节码大小。
- 每次调用开销扩展:5.9µs 至 49.9µs(从最小合约到最大合约增加 8.5 倍)
- 执行块细分:隔离代码读取、账户读取、EVM 执行和数据库写入
所有这些见解都有助于做出更明智的决策,从而大大简化了ACD的决策流程。
2. 核心指标
指标按类别进行组织,涵盖The Block执行生命周期:
| 类别 | JSON 路径 | 描述 |
|---|---|---|
| 区块信息 | block.* | 区块编号、哈希值、gas 消耗量、交易次数 |
| 定时 | timing.* | 执行、验证、提交和总耗时(毫秒) |
| 吞吐量 | throughput.* | 兆气体/秒处理速率 |
| 州阅读 | state_reads.* | 账户、存储和代码读取操作 |
| 州政府撰写 | state_writes.* | 账户和存储变更 |
| 缓存 | cache.* | 账户、存储和代码缓存的命中率/未命中率 |
指标定义
| 指标 | 类型 | 描述 |
|---|---|---|
block.number | int64 | 方块高度 |
block.hash | 细绳 | 区块哈希(以 0x 为前缀) |
block.gas_used | int64 | 总燃气消耗量 |
block.tx_count | int32 | 交易次数 |
timing.execution_ms | int64 | 执行交易所花费的时间 |
timing.state_read_ms | int64 | 读取状态(账户、存储槽位和合约代码)所花费的时间 |
timing.state_hash_ms | int64 | 花费在状态重排上的时间 |
timing.total_ms | int64 | 总块处理时间 |
throughput.mgas_per_sec | float64 | Gas吞吐量(gas_used / execution_time / 1e6) |
state_reads.accounts | int64 | 账户数据加载(余额、随机数、代码哈希) |
state_reads.storage_slots | int64 | 存储槽读取 |
state_reads.code | int64 | 合约字节码读取 |
state_reads.code_bytes | int64 | 读取的代码总字节数 |
state_writes.accounts | int64 | 账户状态更新 |
state_writes.storage_slots | int64 | 存储槽写入 |
state_writes.code | int64 | 合约字节码写入 |
state_writes.code_bytes | int64 | 代码写入总字节数 |
cache.{type}.hits | int64 | 账户/存储/代码的缓存命中次数 |
cache.{type}.misses | int64 | 缓存未命中(需要读取数据库) |
cache.{type}.hit_rate | float64 | 命中率:( (hits / (hits + misses)) * 100.0 |
3. 慢速块 JSON 格式
当代码块执行时间超过可配置Threshold(默认值:1000毫秒)时,客户端会输出结构化的JSON日志:
{ "level" : "warn" "msg" : "Slow block" "block" : { "number" : 19234567 "hash" : "0x1234...abcd" "gas_used" : 29500000 "tx_count" : 234 } "timing" : { "execution_ms" : 1250 "state_read_ms" : 320 "state_hash_ms" : 150 "commit_ms" : 75 "total_ms" : 1475 } "throughput" : { "mgas_per_sec" : 23.60 } "state_reads" : { "accounts" : 5420 "storage_slots" : 12340 "code" : 890 "code_bytes" : 456000 } "state_writes" : { "accounts" : 234 "storage_slots" : 1890 } "cache" : { "account" : { "hits" : 4800 , "misses" : 620 , "hit_rate" : 88.60 } "storage" : { "hits" : 10200 , "misses" : 2140 , "hit_rate" : 82.68 } "code" : { "hits" : 870 , "misses" : 20 , "hit_rate" : 97.75 } } }现场要求
| 场地 | 必需的 | 笔记 |
|---|---|---|
level , msg | ![]() | 必须是"warn"和"Slow block" |
block.* | ![]() | 所有模块信息字段均为必填项 |
timing.execution_ms | ![]() | 核心时序指标 |
timing.state_read_ms | ![]() | 从数据库/缓存读取状态所花费的时间 |
timing.state_hash_ms | ![]() | 花费在 Merkle trie 重述上的时间 |
timing.commit_ms | ![]() | 花费在将状态持久化到存储上的时间 |
timing.total_ms | ![]() | 端到端块处理时间 |
throughput.mgas_per_sec | ![]() | 保留两位Decimal |
state_reads.* | ![]() | 所有读取计数器 |
state_writes.* | ![]() | 所有写入计数器 |
cache.* | ![]() | 嵌套结构,包含命中/未命中/命中率 |
4.实施情况
5. 进一步改进
中期目标:进行更细致的绩效分析
| 改进 | 描述 | 理由 |
|---|---|---|
| 单笔交易指标 | 每个交易的时间和状态访问权限 | 找出导致速度减慢的具体交易。 |
| EVM 操作码计数 | 加载、存储、调用、创建、EXTCODECOPY | 了解执行模式,检测拒绝服务攻击向量。 |
| 唯一访问跟踪 | 唯一账户、存储空间、合约 | 衡量状态访问多样性和工作集大小 |
| 预编译分解 | 预编译计时(ecrecover、sha256、modexp) | 识别昂贵的加密操作 |
| 默克尔化时间 | 账户树与存储树的重新哈希 | 精确确定状态根的计算成本 |
| 内存/分配统计信息 | 峰值内存使用量,每个内存块的分配量 | 跟踪内存压力以进行资源规划 |
| Trie 深度统计 | 平均/最大trie遍历深度 | 了解状态膨胀的影响 |
| 并行执行指标 | 线程利用率、争用统计信息 | 对于并行 EVM 实现 |
| 目击者人数 | Verkle/无状态见证数据大小 | 为无国籍客户做准备 |
| 寒冷与温暖的通道 | 区分首次访问和缓存访问 | EIP-2929 气体分析 |
这篇文章的目的不仅在于告知大家相关信息,更重要的是征求大家的反馈。我们想知道,对于团队、研究人员以及其他任何人来说,还有哪些信息值得我们去挖掘/了解。例如,客户难以收集的内部数据等等。
此外,我们也想知道我们可以用它做些什么,我们有一些想法,并列出了一些目标。但我们相信,人们会比我们想到更多好点子。
谢谢。





