Nautilus Polymarket 适配器基准测试:入站管道、EIP-712 签名与执行流水线的性能剖析与复现指南
2026/9/10 14:23:13 网站建设 项目流程

Nautilus Polymarket 适配器基准测试:入站管道、EIP-712 签名与执行流水线的性能剖析与复现指南

【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader

导读

本文以 NautilusTrader 仓库中 crates/adapters/polymarket/benches/BENCHMARKS.md 为骨架,完整呈现 Polymarket 预测市场适配器(nautilus-polymarketcrate)的官方基准测试基线:从原始 WebSocket/REST 帧字节到 Nautilus 领域类型的入站管道、订单簿有效增量(effective delta)计算、执行流水线(订单提交/撤单的 JSON 序列化与签名)以及加密路径(EIP-712 与 L2 HMAC-SHA256)的逐层耗时数据。读完本文,你将掌握这套基准的分层设计意图、每条数字对应的底层源码实现,以及如何在性能主机上复现并获得可对比的测量结果。


测量基线:环境、profile 与噪声控制

该基准报告记录的是 2026-08-14 在提交0ea286ec6d上测得的一组数字,测量主机为AMD Ryzen Threadripper 9980X,运行 Linux7.0.0-28-generic内核与rustc 1.97.1。报告开头明确声明了两条使用纪律:

  • 绝对数字因机器而异,只有同机(same-machine)对比的增量才有意义
  • 在实质性性能变更后或发布前需要刷新报告,并更新日期。

测量配置是这套数字可信度的关键,包含三个层面:

控制项取值作用
编译 profilebench-lto使用 release 优化,开启lto = "fat"codegen-units = 1debug = "full",与生产 release 二进制对齐
CPU 调频策略performancegovernor消除动态调频导致的时钟抖动
地址空间布局随机化每个 benchmark 进程禁用 ASLR(setarch -R消除 ASLR 造成的缓存/分支预测噪声

bench-ltoprofile 定义在工作区根 Cargo.toml 中:它继承releaseopt-level = 3),额外设置debug = "full"strip = false以保留供perf/cargo flamegraph使用的调试符号,同时以lto = "fat"+codegen-units = 1复刻生产二进制的链接优化行为。与之相对,默认的cargo bench使用的benchprofile(Cargo.toml)同样继承 release 并保留完整调试信息,但关闭 LTO以加快本地迭代编译——因此本地临时对比用bench,对外发布数字(PR 描述、release notes、各适配器BENCHMARKS.md)一律用bench-lto。这一约定来自仓库根目录的 BENCHMARKING.md 中"Measurement requirements"一节的明确要求。

复现命令

sudo cpupower frequency-set -g performance CARGO_BUILD_JOBS=16 setarch "$(uname -m)" -R \ cargo bench -p nautilus-polymarket --profile bench-lto \ --bench data --bench effective_deltas --bench exec --bench micros --bench signing sudo cpupower frequency-set -g powersave # restore default

逐段解读:

  • cpupower frequency-set -g performance:把 CPU 调频器切到 performance 模式,先设置再复测,最后恢复 powersave;
  • CARGO_BUILD_JOBS=16:限制并行编译任务数,避免构建期负载污染后续测量;
  • setarch "$(uname -m)" -R:为子进程禁用 ASLR;
  • --profile bench-lto:使用与生产 release 对齐的发布测量 profile;
  • 五个--bench目标(dataeffective_deltasexecmicrossigning)对应 crates/adapters/polymarket/Cargo.toml 中注册的五个[[bench]]条目(均设置harness = false,由 Criterion 接管)。

基准测试的整体政策、工具选型(Criterion / iai / CodSpeed / flamegraph)与噪声抑制完整配方见仓库根目录 BENCHMARKING.md,其中还详细定义了两种 profile 的适用场景。

基准套件全景:五个 Criterion 目标的分层设计

这套基准的核心设计思想是分层解耦:每条流水线基准(pipeline bench)给出"端到端单条消息成本",而组件基准(component bench)把流水线数字拆解到decodeparse、原子构造三个粒度,用于在流水线数字回退(regression)时快速定位时间花在了哪一层。五个目标的关系如下:

Bench 目标测量范围排除项
data原始帧字节 → Nautilus 领域类型(decode + parse + cache 查找 + 类型构造)无 I/O、无 async runtime、无 channel、无订阅状态、无簿应用、无发射
effective_deltas已解析的OrderBookDeltas+ 已填充的 L2 MBP 簿 → 更新后的簿与有效领域批次Criterion 在计时区外克隆种子簿
exec已解析订单输入 → 请求 JSON 体 + L2 HMAC-SHA256 签名远端拉取、JSON decode、auth_headers的固定开销
signingEIP-712 订单签名与 L2 HMAC 的各组成部分
microsdecode/parse/原子的组件级拆解

所有 fixture 以编译期&'static str常量或include_str!内联进 benches/common/mod.rs,运行时不读文件系统;共享的 HTTP 与用户通道 fixture 则从 test_data 目录编译期引入(如ws_user_order_msg.jsonws_user_order_fok_killed.jsonws_user_trade_msg.jsonws_user_batch_msg.jsonclob_book_response.json)。

价格变化分发(data.rs:dispatch 组)

该基准解码一个包含六个 price_change的帧,六个变化在两个不同 instrument 之间交错排列,最终产生两个原子的OrderBookDeltas批次。覆盖范围包括:帧时间戳解析、instrument 查找、按 instrument 分组、十进制解析与领域构造;明确排除 JSON decode、订阅状态、簿应用、channel 发射与网络 I/O。

BenchMedianThroughput
dispatch/price_change_interleaved367 ns16.3 M changes/s

对照源码 benches/data.rs 可以还原这条路径的耗时构成:dispatch_book_deltas先用AHashMap(ahash)按asset_id → instrument元数据查表,再用一个group_indices映射把交错的变化按 instrument 分组到groups桶中,最后对每个组调用parse_book_deltas(src/websocket/parse.rs)解析出OrderBookDelta列表并封装为OrderBookDeltas批次。Throughput::Elements(6)把吞吐量折算为每秒处理的变化数。这一行代表的是分组与解析的成本,而非端到端适配器或网络延迟。

入站管道(data.rs:inbound_pipeline 组)

入站管道基准把"原始 WS 帧字节(市场通道)或 REST 行(用户通道)→ Nautilus 领域类型"作为一个整体测量:覆盖 decode + parse + cache 查找 + Nautilus 类型构造,同样无 I/O、无 async runtime、无 channel。行序刻意从最基础的行情流(簿增量)向下排到快照变体、由快照派生的最优买卖报价流、成交,最后是用户通道报告。

BenchMedianThroughput
inbound_pipeline/book_deltas471 ns2.12 M/s
inbound_pipeline/book_snapshot1.36 µs734 k/s
inbound_pipeline/quote_from_snapshot1.01 µs985 k/s
inbound_pipeline/quote_from_price_change532 ns1.88 M/s
inbound_pipeline/trades423 ns2.37 M/s
inbound_pipeline/order_event601 ns1.66 M/s
inbound_pipeline/order_fill1.22 µs818 k/s
inbound_pipeline/order_fill_maker1.15 µs867 k/s

各行的实现路径如下(对应 benches/data.rs 中同名 bench 函数):

  • book_deltasMarketWsMessage::parse解码event_type: price_change帧,取首个变化走parse_book_deltas。解析器对size == 0的变化生成BookAction::Delete,否则生成BookAction::Update(见 src/websocket/parse.rs),最后一个成功的 delta 携带F_LAST标志。
  • book_snapshotparse_book_snapshot把快照转成一个OrderBookDeltas:先压入OrderBookDelta::clear(携带F_SNAPSHOT),再按 bids/asks 逐档追加BookAction::Add,且每条快照 delta 都带F_SNAPSHOT,仅最后一条带F_LAST(src/websocket/parse.rs)。这是该行比增量行慢的主要来源:5 档 bids + 5 档 asks + 1 条 clear 共 11 个 delta 的构造。
  • quote_from_snapshot/quote_from_price_change:从快照或 price_change 帧的best_bid/best_ask字段推导QuoteTick。注意 Polymarket 的 bids 升序、asks 降序排列,最优档在数组末尾snap.bids.last());price_change 路径在两侧缺失且drop_quotes_missing_side启用时返回None,并拒绝锁死/交叉盘口(src/websocket/parse.rs)。
  • tradesparse_trade_tick构造TradeTick,其trade_iddetermine_trade_id用 FNV-1a 从(asset_id, side, price, size, timestamp)派生(见下文"优化笔记")。
  • order_event/order_fill/order_fill_maker:用户通道的 WS → report 转换是 dispatch 循环的私有实现,因此这三个基准改用 RESTGET /ordersGET /trades的解析路径作为等价代表——两条路径共享字符串十进制 + 状态解析逻辑。order_fill使用非零 taker 费率与指数,因此包含了当前费率曲线;order_fill_maker覆盖一条 maker leg 及其复合 trade ID,但不含私有 WS dispatch 的跟踪器与发射器开销(详见 benches/data.rs 与 src/execution/parse.rs)。

有效增量处理(effective_deltas.rs

该基准测量的是"已解析的OrderBookDeltas+ 已填充的 L2 MBP 订单簿 → 更新后的簿 + 有效领域批次"这一状态迁移。Criterion 使用iter_batched_ref计时区外克隆种子簿,确保测的是真实生产工作而非 fixture 重置。快照深度按每侧计算,因此 depth 100 意味着 200 个价格档位。

BenchEstimate
effective_deltas/snapshot/unchanged/102.64 µs
effective_deltas/snapshot/ten_percent_resized/102.76 µs
effective_deltas/snapshot/ten_percent_replaced/102.81 µs
effective_deltas/snapshot/unchanged/10032.6 µs
effective_deltas/snapshot/ten_percent_resized/10032.5 µs
effective_deltas/snapshot/ten_percent_replaced/10033.0 µs

三个变化场景分别模拟:快照与原簿完全相同(unchanged)、10% 档位尺寸变化(resized)、10% 档位价格替换(replaced)。实现核心是 src/data/effective_deltas.rs 的apply_snapshot_and_diff

  1. 校验 instrument 匹配;当簿类型非L2_MBP或快照含无侧 delta 时走克隆回退路径apply_snapshot_and_diff_fallback
  2. 快速路径先用level_sizes把新旧簿两侧的价格 →(size, index)收集进IndexMap,然后apply_deltas_unchecked应用快照;
  3. compute_effective_deltas对新簿逐档与旧簿做差:同价不同尺寸 →Update,同价同尺寸 → 跳过,新价格 →Add;旧簿中残留(被快照移除)的档位按原索引排序后追加Delete;批次末尾的 delta 打上F_LAST;无差异时返回None

从数据看,depth 从 10 涨到 100 时成本几乎线性放大(约 12 倍),而三种变化场景在同深度下差异极小——因为差集计算的支配项是对整本簿的遍历与IndexMap操作,与具体差异比例关系不大。

执行流水线(exec.rs

执行流水线基准测量"已解析的订单输入 → 每次请求的 JSON 体 + L2 HMAC-SHA256 签名",覆盖:市场簿交叉价格(crossing price)计算、maker/taker 数量数学、EIP-712 订单签名(仅提交)、JSON 体序列化以及auth_headers通过Credential::sign附加的 HMAC 体签名。市场提交行从解码后的真实 CLOB 簿档位出发(fixtureclob_book_response.json),省略远端拉取与 JSON decode;auth_headers在签名之外的固定成本(时间戳字符串格式化 + 五个POLY_*头)也被排除——它是与这些基准要捕捉的回退无关的常量开销。此外,Polymarket CLOB 不支持就地修改(modify),cancel-replace 是两个独立操作,因此没有modify行。

BenchMedianThroughput
exec_pipeline/submit_limit48.3 µs20.7 k/s
exec_pipeline/submit_market49.0 µs20.4 k/s
exec_pipeline/submit_limit_neg_risk48.8 µs20.5 k/s
exec_pipeline/cancel254 ns3.93 M/s

源码层面(benches/exec.rs)每条提交路径的公共骨架是:PolymarketOrderBuilder构造订单 →serialize_post_order序列化PostOrderBody(镜像 crate 私有的http::clob::PostOrderBody的 wire 形状)→Credential::sign计算 HMAC。市场提交额外调用calculate_market_price走读解码簿找交叉价格,再用adjust_market_buy_amount做含费的 BUY 数量调整(src/execution/parse.rs)。负风险(neg-risk)市场被单独固定成一行,因为verifyingContract的选择在 EIP-712 哈希内部——若把它混在标准 CTF 用例里,负风险路径的回退会被静默掩盖。

cancel只需 254 ns,原因在于撤单不需要 EIP-712 签名,客户端成本仅是 JSON 序列化 + HMAC。生产环境里网络往返时间仍占主导。

加密路径(signing.rs

Polymarket 有两套签名面:L1 EIP-712 订单签名(热路径上的OrderSigner::sign_order)与L2 HMAC-SHA256 请求签名(每个已认证 REST 调用的Credential::sign)。该基准把 EIP-712 的成本(typed-data 哈希 + ECDSA)从 HMAC 路径中拆解出来,使任一侧的回退都可定位。

BenchMedian
sign_order47.4 µs
sign_order_neg_risk47.2 µs
sign_order_poly_127149.0 µs
order_hash2.78 µs
signer_construction34.2 µs
sign_clob_auth81.6 µs
hmac_l2_sign210 ns

对应实现(benches/signing.rs 与 src/signing/eip712.rs):

  • sign_order系列:OrderSigner::sign_order计算订单的 EIP-712 typed-data 哈希(keccak256,约 2.78 µs,即order_hash行)并对 CTF Exchange 合约做 secp256k1 ECDSA。47 µs 级别的成本几乎全部来自 ECDSA 签名本身
  • sign_order_neg_risksign_order_poly_1271:负风险路径切换verifyingContractNEG_RISK_CTF_EXCHANGENEG_RISK_CTF_COLLATERAL_ADAPTER(src/signing/eip712.rs);Poly1271使用扩展签名编码与存款钱包地址;
  • signer_construction(34.2 µs):OrderSigner::new从十六进制私钥构造PrivateKeySigner(alloy 的SignerSync/local::PrivateKeySigner);
  • sign_clob_auth(81.6 µs):CLOB/auth/api-key/auth/derive-api-key引导流程使用的钱包签名。它每次调用都从 hex 私钥新建PrivateKeySigner(约 34 µs 的隐藏开销,与signer_construction完全吻合);
  • hmac_l2_sign(210 ns):Credential::sign的裸 HMAC-SHA256 成本——签名密钥在Credential::new时一次性初始化(hmac::Key::new),随后Context::update流式写入{timestamp}{method}{request_path}{body}四段消息而不分配拼接字符串(src/common/credential.rs)。

组件分解(micros.rs

诊断型基准,把上述流水线数字拆解到更细粒度。当某条流水线回退时,用这些数字定位时间去向;当评估结构性改动(如替换 JSON tokenizer)时,用它确认收益落在预期的层。

BenchMedian
decode_only/trade274 ns
decode_only/book902 ns
decode_only/price_change415 ns
decode_only/user_order978 ns
decode_only/user_order_captured952 ns
decode_only/user_order_dispatch1.05 µs
decode_only/user_trade639 ns
decode_only/user_batch2.15 µs
parse_only/trade160 ns
parse_only/book_snapshot385 ns
parse_only/book_deltas40.4 ns
atom/decimal_from_str7.70 ns
atom/price_from_decimal_dp11.7 ns
atom/quantity_from_decimal_dp8.22 ns
atom/price_combined18.7 ns
atom/compute_commission119 ns
atom/adjust_market_buy_amount209 ns
atom/trade_id_determine108 ns
atom/uuid4_new14.5 ns
atom/event_filled_construct19.0 ns
atom/event_accepted_construct15.5 ns

对照 benches/micros.rs 可得出若干定位结论:

  • decode 层是入站成本的主体:如book的 decode(902 ns)占book_snapshot流水线(1.36 µs)约三分之二;tradedecode(274 ns)也明显高于其 parse(160 ns);
  • parse 层非常轻book_deltas的 parse 单条仅 40.4 ns,说明字符串十进制解析不是增量路径的瓶颈;
  • 原子构造是纳秒级decimal_from_str7.7 ns、price_combined(字符串 → Price 全路径)18.7 ns、event_filled_construct19.0 ns,全部在流水线绝对数字中可忽略;
  • compute_commission119 ns是费用曲线在每次成交报告里的固定开销;adjust_market_buy_amount209 ns 是市场买入的含费调整;trade_id_determine108 ns 是 FNV-1a 哈希。

优化笔记深度解读

报告最后的 Notes 部分是这份文档最有价值的工程沉淀,结合源码逐条展开如下。

1. 入站 decode 规避了 Serde 的 tagged content buffer

市场 fixture 与合成用户 fixture 把event_type放在首位(tag-first),而捕获的 FOK 订单把它放在末位(tag-last)。生产解析器对 tag-first 消息一趟解码,对乱序的单条消息使用 tag 扫描 + 直接类型化 decode。效果:LTO 市场管道提升 26%~36%;同会话基线对比下,tag-first 用户订单与成交 fixture 提升约 39%~40%,捕获的 tag-last 订单提升 9.5%,其 handler 分发路径提升 16.7%。user_batch行使用 tag-last 元素与通用的派生批次解析器。

2. 字符串 → Price/Quantity 是 Decimal 直达,跳过 f64

parse_priceparse_quantity(src/websocket/parse.rs)先经parse_decimal_exact(即Decimal::from_str的精确语义变体,处理科学计数法)再走Price::from_decimal_dp,与 hyperliquid 适配器一致。所有 Decimal 类型的 REST 字段(PolymarketOpenOrderPolymarketTradeReportPolymarketMakerOrder)与 WS 用户通道字符串字段都完全跳过中间的 f64 解析。组合的字符串 → Price 路径约 18.7 ns,同时消除了浮点舍入风险。

3. 带费用的成交已纳入测量

order_fill使用非零 taker 费率与指数,因此包含了当前费用曲线。compute_commission约 119 ns,其公式为fee = rate × (p × (1 − p))^exponent,仅 taker 支付、结果四舍五入到 5 位小数(src/execution/parse.rs)。order_fill_maker覆盖一条 maker leg 及其复合 trade ID,但不含私有 WS dispatch 的跟踪器与发射器工作。

4. 执行提交被 EIP-712 绑定

sign_order约 47 µs,支配每一条exec_pipeline/submit_*行;LTO 折叠了各形状间的差异,使 limit、market、neg-risk 收敛在 48~49 µs。市场行还包含解码簿的价格走读与含费的 BUY 尺寸调整。其余工作是 maker/taker 数量数学、builder 状态、JSON 序列化与 L2 HMAC 步骤。不改变 EIP-712 + keccak + secp256k1 路径的优化不会移动这些数字——这是判断执行路径优化空间的最重要结论。

5. POLY_1271 有独立的签名基线

其扩展签名编码在共享的 secp256k1 成本之外只增加很少开销(49.0 µs vs 47.4 µs)。

6.cancel受 HMAC 约束

REST 撤单不需要 EIP-712 签名,客户端成本 = JSON 体序列化 + L2 HMAC-SHA256 签名(Credential::sign)。Credential一次性初始化 HMAC 密钥,并流式写入四段消息而不分配组合字符串;生产环境的网络往返仍主导墙钟时间。

7.sign_clob_auth携带隐藏的 signer 构造

该函数每次调用都从 hex 私钥新建PrivateKeySigner(约 34 µs 开销,恰等于signer_construction成本),然后才签名。此路径是冷路径(仅用于凭据引导时的 CLOB/auth/api-key/auth/derive-api-key流程),因此不是生产热点;若它将来进入热路径,应接受预构造的 signer 而非每次重建。

8.trade_id_determine(108 ns)是 FNV-1a

Polymarket 的last_trade_price事件不发布 trade ID,适配器从(asset_id, side, price, size, timestamp)派生确定性 ID。FNV-1a 64 位哈希跨架构与 crate 版本稳定,且用0x1f分隔符防止变长字段碰撞(如"0.12"+"34""0.1"+"234")(src/common/parse.rs)。这保证了重连后 trade ID 可复现。

9. 交错 price-change 分发避免了逐变化克隆

相对优化父提交c6bb45e0a7,同一六变化 fixture 与对照组从 638 ns / 9.40 M changes/s 提升到 367 ns / 16.3 M changes/s——延迟降低 42.5%,吞吐提升 73.9%。父会话估计区间 597~641 ns,优化会话 365~369 ns。结果覆盖分组与解析,而非端到端适配器或网络延迟。

10. 真实用户 WS 分发仍是分析边界

price-change 行镜像了生产的分组与解析工作,但不调用 crate 私有的 router、保留状态、簿应用与 emitter。套件分别测量用户消息解码与两种公开 report builder,因此把"解码"与"私有分发"两个剖面边界明确分开。

如何使用这套基准

对读者而言,这套文档的实用价值体现在三个场景:

  1. 复现与验证:在性能主机上按上文命令运行,先确认自己的基线数字与报告同量级,再讨论任何增量;
  2. 优化前定位:流水线回退时,按micros.rs的 decode → parse → atom 三层拆解定位;评估签名相关改动时,认准signing.rs中 EIP-712(47 µs 级)与 HMAC(210 ns 级)的数量级差异;
  3. 发布数字的纪律:对外引用任何数字前,使用--profile bench-lto、performance governor 与禁用 ASLR,并遵循 BENCHMARKING.md 中"记录测量上下文(CPU 型号、内核、工具链、构建 profile)"的要求——正如本报告在开头所做的那样。

最后提醒:本报告的绝对数字仅对 2026-08-14 的0ea286ec6d提交有效;任何实质性性能变更后都需要刷新基线并更新日期,且只有同机对比的增量才有意义

【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询