news 2026/9/10 22:15:02

nautilus_trader Lighter 适配器基准测试:Schnorr 签名、Poseidon2 哈希与 Go 官方栈的性能对比全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
nautilus_trader Lighter 适配器基准测试:Schnorr 签名、Poseidon2 哈希与 Go 官方栈的性能对比全指南

nautilus_trader Lighter 适配器基准测试:Schnorr 签名、Poseidon2 哈希与 Go 官方栈的性能对比全指南

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

本篇技术指南围绕 crates/adapters/lighter/benches/BENCHMARKS.md 展开,系统讲解 Nautilus 交易引擎中 Lighter L2 适配器的基准测试方法论、可复现的测量流程、已发布的签名性能基线,以及 Rust 实现与官方 Go 栈(lighter-go+poseidon_crypto)的逐项对比结论。读完本文,你将掌握如何在本机复现同一组数字、如何正确解读并引用这些基准,以及 Lighter 签名热路径(Schnorr 签名 / 验签 / 公钥派生 / 交易哈希)在 Rust 侧的底层成本构成。

一、基准测试的目标与定位

Lighter 是一个基于 Rust 的高性能 L2 交易协议,nautilus_trader 通过nautilus-lightercrate 与其对接(见 crates/adapters/lighter/Cargo.toml 中的nautilus-lighter包声明)。L2 链上交易的提交路径包含 Poseidon2 哈希与 Schnorr 签名等密码学原语,这是策略下单到 L2 排序器之间的关键路径,其性能直接决定高频策略的极限下单频率。

仓库根目录的 BENCHMARKING.md 明确了项目基准测试的整体政策:基准即文档,它记录了什么被认为是热路径、什么样的输入被认为是真实的、以及某一时刻的成本形态;性能声明必须附上可复现的证据。Lighter 适配器的benches/BENCHMARKS.md正是这一政策在该适配器上的落地——它是本适配器唯一被授权对外引用的签名数字来源

  • 发布签名相关性能数字时,应引用signing_sign_verify.rs的结果;
  • 排查数据/执行管道回归时,使用data.rsexec.rsmicros.rs的组合定位瓶颈所在的层级;
  • 指令数级回归门槛由signing_field_iai.rs提供。

二、测量环境:谁测的、在什么条件下测的

数字只有在可复现的条件下才有意义。BENCHMARKS.md记录的本轮测量环境如下:

项目取值
测量日期2026-08-19
CPUAMD Ryzen Threadripper 9980X
工具链rustc 1.97.1,bench-ltoprofile
Rust profilerelease 优化 +lto = "fat"+codegen-units = 1+debug = full
CPU 调频策略固定为performance
ASLR通过setarch -R关闭
Rust 数据口径两次测量会话的中位 Criterion 中点(先跑一次预热)
Go 对照Go 1.26.6,-cpu=1,同样的调频与 ASLR 控制,取两次会话的ns/op中位数

bench-ltoprofile 定义在仓库根 Cargo.toml:inherits = "release"debug = "full"strip = falselto = "fat"incremental = falsecodegen-units = 1。它的设计意图是让基准数字与生产 release profile 的代码生成质量一致(lto = "fat"codegen-units = 1),同时保留调试符号供剖析器使用。Cargo.toml 中对应注释(Cargo.toml)明确要求:任何对外发布/引用的基准数字(PR 描述、release note、各适配器BENCHMARKS.md)都必须使用该 profile,并配合setarch -R关闭 ASLR、将 CPU 调频器设为performance以获得最紧致、最可复现的数据。

重要前提:绝对数值因机器而异,只有同机对比的差值才有意义。文档明确说明测量期间该主机还有其他交互会话,噪声地板偏高,因此应当比较的是比率(ratio)而不是绝对微秒数。

三、如何复现:完整操作步骤

复现流程的核心思想是:先一次性施加主机控制项,然后在控制项保持生效期间依次运行 Rust 与 Go 两侧,最后恢复调频策略。

3.1 主机控制项

sudo cpupower frequency-set -g performance

setarch "$(uname -m)" -R用于在计时进程两侧同时关闭 ASLR。taskset是可选的额外隔离手段,本轮数字未使用。

3.2 Rust 签名基线

CARGO_BUILD_JOBS=16 setarch "$(uname -m)" -R cargo bench --locked \ --profile bench-lto -p nautilus-lighter --bench signing_sign_verify

该命令需完整运行三次:第一次结果作为预热丢弃,报告后两次测量会话的中位数。这里的--bench signing_sign_verify对应 crates/adapters/lighter/Cargo.toml 中注册的[[bench]]目标,harness = false表示由 Criterion 自带测试框架接管。同一文件中还注册了signing_poseidon2signing_fieldsigning_curvesigning_field_iaidataexecmicros共八个 bench 目标。

3.3 Go 对照(官方栈对比)

crate 不随仓库分发 Go 源码,需要在仓库外重建两个 scratch 模块,粘贴文档给出的清单后运行。需要 Go 1.23+ 工具链;若go version缺失,从官方渠道安装。

  • 原始原语套件elliottech/poseidon_crypto固定到fbd3713966eeeb9496166db9b599d4a3bb7b9e2b——与 crate 的 fixture 向量同一修订版本。输入与common/mod.rs中的字节完全一致。
  • SDK 套件elliottech/lighter-go固定到cef81af980850607a66213fcca5f3e76ddebda7e(即v1.0.9-0.20260812092842-cef81af98085),该模块依赖poseidon_cryptov0.0.15。其中ConstructCreateOrderTx对与 Rustcreate_order_tx()相同的 create-order 字段执行校验、哈希与签名。官方 Go 客户端通过poseidon_crypto在进程内签名,不会加载lighter-python使用的闭源共享库——这一点保证了对比的是纯公开算法实现。

保持 governor 为performance,创建 scratch 目录树,粘贴各文件、go mod tidy,每套件各跑两次,取ns/op中位数:

mkdir -p /tmp/lighter-signing-go/primitives /tmp/lighter-signing-go/sdk # paste go.mod and bench_test.go into each directory cd /tmp/lighter-signing-go/primitives go mod tidy setarch "$(uname -m)" -R go test -bench=. -benchtime=3s -cpu=1 cd /tmp/lighter-signing-go/sdk go mod tidy setarch "$(uname -m)" -R go test -bench=. -benchtime=3s -cpu=1 sudo cpupower frequency-set -g powersave
原始原语套件清单

/tmp/lighter-signing-go/primitives/go.mod

module lighter-signing-primitives go 1.23 require github.com/elliottech/poseidon_crypto v0.0.0-20260410093228-fbd3713966ee

/tmp/lighter-signing-go/primitives/bench_test.go

package primitives import ( "testing" curve "github.com/elliottech/poseidon_crypto/curve/ecgfp5" g "github.com/elliottech/poseidon_crypto/field/goldilocks" gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension" schnorr "github.com/elliottech/poseidon_crypto/signature/schnorr" ) func fixedSk() curve.ECgFp5Scalar { bytes := []byte{ 0x0b, 0x8e, 0x0f, 0x63, 0xc2, 0x4d, 0x8b, 0xaa, 0xcd, 0x9d, 0x29, 0xad, 0x4e, 0x9a, 0x4b, 0x73, 0xc4, 0xa8, 0xd2, 0xbb, 0x8b, 0x16, 0xdc, 0x4f, 0xa9, 0xd7, 0xc2, 0xe1, 0xd3, 0xa8, 0xb1, 0xf0, 0xe8, 0xd3, 0xa4, 0xc5, 0xb6, 0xe7, 0xf0, 0x01, } return curve.ScalarElementFromLittleEndianBytes(bytes) } func fixedK() curve.ECgFp5Scalar { var bytes [40]byte bytes[0] = 0x42 bytes[7] = 0x01 bytes[16] = 0x91 bytes[24] = 0x37 return curve.ScalarElementFromLittleEndianBytes(bytes[:]) } func fixedHashedMsg() gFp5.Element { return gFp5.Element{ g.GoldilocksField(0x0123_4567_89AB_CDEF), g.GoldilocksField(0xFEDC_BA98_7654_3210), g.GoldilocksField(0x1111_2222_3333_4444), g.GoldilocksField(0x5555_6666_7777_8888), g.GoldilocksField(0x0000_0001_0000_0001), } } func fixedPk() gFp5.Element { return schnorr.SchnorrPkFromSk(fixedSk()) } func fixedSignature() schnorr.Signature { return schnorr.SchnorrSignHashedMessage2(fixedHashedMsg(), fixedSk(), fixedK()) } var ( sinkSig schnorr.Signature sinkBool bool sinkPk gFp5.Element ) func BenchmarkSchnorrSign(b *testing.B) { sk := fixedSk() k := fixedK() msg := fixedHashedMsg() b.ResetTimer() for i := 0; i < b.N; i++ { sinkSig = schnorr.SchnorrSignHashedMessage2(msg, sk, k) } } func BenchmarkSchnorrVerify(b *testing.B) { pk := fixedPk() msg := fixedHashedMsg() sig := fixedSignature() b.ResetTimer() for i := 0; i < b.N; i++ { sinkBool = schnorr.IsSchnorrSignatureValid(pk, msg, sig) } } func BenchmarkSchnorrPkFromSk(b *testing.B) { sk := fixedSk() b.ResetTimer() for i := 0; i < b.N; i++ { sinkPk = schnorr.SchnorrPkFromSk(sk) } }
官方 SDK 套件清单

/tmp/lighter-signing-go/sdk/go.mod

module lighter-signing-sdk go 1.23.0 require github.com/elliottech/lighter-go v1.0.9-0.20260812092842-cef81af98085

/tmp/lighter-signing-go/sdk/bench_test.go

package sdk import ( "testing" "github.com/elliottech/lighter-go/signer" "github.com/elliottech/lighter-go/types" "github.com/elliottech/lighter-go/types/txtypes" ) const ( chainID uint32 = 304 accountIndex int64 = 12345 apiKeyIndex uint8 = 5 nonce int64 = 42 expiredAt int64 = 1_777_809_907_000 ) func fixedSkBytes() []byte { return []byte{ 0x0b, 0x8e, 0x0f, 0x63, 0xc2, 0x4d, 0x8b, 0xaa, 0xcd, 0x9d, 0x29, 0xad, 0x4e, 0x9a, 0x4b, 0x73, 0xc4, 0xa8, 0xd2, 0xbb, 0x8b, 0x16, 0xdc, 0x4f, 0xa9, 0xd7, 0xc2, 0xe1, 0xd3, 0xa8, 0xb1, 0xf0, 0xe8, 0xd3, 0xa4, 0xc5, 0xb6, 0xe7, 0xf0, 0x01, } } func createOrderReq() *types.CreateOrderTxReq { return &types.CreateOrderTxReq{ MarketIndex: 1, ClientOrderIndex: 7, BaseAmount: 1_000_000, Price: 25_000_000, IsAsk: 0, Type: txtypes.LimitOrder, TimeInForce: txtypes.ImmediateOrCancel, ReduceOnly: 0, TriggerPrice: 0, OrderExpiry: txtypes.NilOrderExpiry, } } func transactOpts() *types.TransactOpts { account := accountIndex apiKey := apiKeyIndex n := nonce return &types.TransactOpts{ FromAccountIndex: &account, ApiKeyIndex: &apiKey, ExpiredAt: expiredAt, Nonce: &n, } } var ( sinkTx *txtypes.L2CreateOrderTxInfo sinkHash []byte ) func BenchmarkConstructCreateOrder(b *testing.B) { key, err := signer.NewKeyManager(fixedSkBytes()) if err != nil { b.Fatal(err) } tx := createOrderReq() ops := transactOpts() if _, err := types.ConstructCreateOrderTx(key, chainID, tx, ops); err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { sinkTx, err = types.ConstructCreateOrderTx(key, chainID, tx, ops) if err != nil { b.Fatal(err) } } } func BenchmarkCreateOrderHash(b *testing.B) { txInfo := types.ConvertCreateOrderTx(createOrderReq(), transactOpts()) if err := txInfo.Validate(); err != nil { b.Fatal(err) } var err error b.ResetTimer() for i := 0; i < b.N; i++ { sinkHash, err = txInfo.Hash(chainID) if err != nil { b.Fatal(err) } } }

两个套件中的固定私钥、固定 nonce、固定哈希消息等 fixture 与 Rust 侧的common/mod.rs完全同源:例如fixed_sk()fixed_k()fixed_hashed_msg()在 Go 与 Rust 中字节级一致,chainID = 304对应 Rust 侧CHAIN_ID常量,CreateOrderTxReq的字段取值(MarketIndex: 1ClientOrderIndex: 7BaseAmount: 1_000_000Price: 25_000_000等)也与 Rustcreate_order_tx()fixture 一一对应。这是两侧结果可比的前提。

文档还提示:噪声抑制的通用方法论与政策详见仓库根的 BENCHMARKING.md。

四、已发布签名基线(signing_sign_verify.rs

signing_sign_verify.rs是 Lighter 适配器对外发布的 L2 签名基线。它测量的正是用户可见的关键路径:PrivateKey::signPublicKey::verify、两种交易热类型的compute_tx_hashsign_tx(哈希 + 签名)、公钥派生以及build_auth_token_at(见 signing_sign_verify.rs)。

关键设计:PrivateKey::signsign_tx都使用固定 noncek,因此计时区域是哈希与曲线运算,而不是随机数生成——排除了 RNG 对测量结果的干扰。fixed_k()在 common/mod.rs 中构造:40 字节标量中仅有 4 个字节非零(0x420x010x910x37),与 Go 侧fixedK()完全一致。

Bench中位数吞吐量
signing/PrivateKey::sign67.1 µs14.9 k/s
signing/PublicKey::verify139 µs7.19 k/s
signing/PrivateKey::public_key64.6 µs15.5 k/s
signing/compute_tx_hash (CreateOrder)1.99 µs503 k/s
signing/compute_tx_hash (CancelOrder)994 ns1.01 M/s
signing/sign_tx (CreateOrder)67.8 µs14.7 k/s
signing/sign_tx (CancelOrder)66.8 µs15.0 k/s
signing/build_auth_token_at67.8 µs14.7 k/s

4.1 从源码看这些数字的构成

  • PrivateKey::sign/PublicKey::verify/public_key定义在 schnorr/key.rs。验证是最昂贵的原语,因为它执行双基标量乘法加上一次 Poseidon2 哈希;签名与公钥派生各自只归约为一次常数时间标量乘法
  • sign_tx定义在 signing/tx/encode.rs,其成本 = 签名(约 65 µs)+ 一次 create-order 哈希(约 2 µs),与上表 67.8 µs 吻合。
  • compute_tx_hash的底层语义在 signing/tx/mod.rs 中有权威说明:每个 L2 交易体按固定顺序规约为一串 Goldilocks 域元素,用 Poseidon2 哈希成单个Fp5摘要,可选地与每笔交易的L2TxAttributes哈希聚合,最终产出 40 字节规范小端字节。排序器会重新计算同一哈希并在不匹配时拒绝交易——这解释了为什么哈希路径是交易提交的关键路径。
  • build_auth_token_at定义在 signing/auth_token.rs,用于 L2 会话鉴权令牌构建,同样走一次签名。

五、与官方 Go 栈的对比结果

工作负载GoRust(bench-lto加速比
Schnorr 签名(固定k247 µs67.1 µs3.7x
Schnorr 验签438 µs139 µs3.2x
公钥派生241 µs64.6 µs3.7x
CreateOrder 哈希4.37 µs1.99 µs2.2x
CreateOrder 哈希 + 签名283 µs(lighter-go67.8 µs4.2x

5.1 如何正确解读这张表

前三行是同一组poseidon_crypto原语,与PrivateKey::signPublicKey::verifyPrivateKey::public_key对应,两侧均使用固定 nonce,因此回答的是曲线与哈希实现本身的成本差

"CreateOrder 哈希" 行是L2CreateOrderTxInfo.Hash对比compute_tx_hash。两侧都使用空属性(empty attributes),因此只运行 body 的 Poseidon2 原像哈希,是纯净的哈希成本对比。

"CreateOrder 哈希 + 签名" 行在两侧并不是同一个函数,必须注意区分:

  • Go 运行types.ConstructCreateOrderTx:校验 → 哈希 → 采样新 nonce → 签名;
  • Rust 运行sign_tx:fixture nonce 已经就位,只做哈希 + 签名。

因此 Go 侧额外包含了真实官方 SDK 的成本(校验与 nonce 采样)。当问题聚焦"曲线与哈希实现的成本"时,应使用固定k的原语行;而当问题聚焦"完整官方 SDK 工作流对比"时,才使用最后一行。

六、其他基准套件:管道与指令数门槛

BENCHMARKS.md明确说明本轮未重新测量以下套件,但它们的职责边界值得了解:

套件覆盖范围用法
data.rs入站管道:原始 WS 帧字节 → Nautilus 域类型每个消息种类端到端(decode + parse + 缓存查找 + 类型构造),无 I/O、无异步运行时、无通道
exec.rs有符号线上报文装配:类型化订单意图 → 可 POST 的签名线字节组装CreateOrderTxInfo/CancelOrderTxInfo/ModifyOrderTxInfo,跑sign_tx,再经TxInfoJson渲染成线上 JSON
micros.rs组件级微基准,将管道数字拆解为构成成本decode_onlyparse_only、原子级 Decimal/UUID/事件构造、签名组件成本
signing_field.rs单操作级墙钟噪声用于原语级观察
signing_field_iai.rs原语指令数门槛(iai)小且确定操作的指令数信号

引用纪律:发布签名数字时引用signing_sign_verify.rsmicros.rs会在解码与 JSON 渲染旁边重复少量相同调用,以便将管道回归定位到具体层级,但不得把这些重复项当作第二个基线

6.1 三个套件的源码级分工

  • 入站管道(data.rs):bench group 名为inbound_pipeline,覆盖 trades、book_deltas、book_depth10、quotes、bars、mark_price、index_price、funding_rate、fill_report、order_status 十个消息种类。所有 fixture 是内联的&'static str常量,形状与线上 Wire 格式一致(见 common/mod.rs 的fixtures模块),且刻意保持单消息种类,使被测成本可归因于一种消息。文档注释还说明这些内联字符串与test_data/ws_*.json现场抓包同形,而抓包 JSON 保留给解析器正确性测试使用。
  • 执行管道(exec.rs):bench group 名为exec_pipeline,覆盖 submit_market、submit_limit、submit_stop_market、cancel、modify 五个场景,测的是"策略命令 → L2 排序器"的完整关键路径。它构建了三类订单 fixture(限价、市价、止损市价,区分order_type0/1/2 与不同time_in_force),并引入了L2TxAttributes(integrator 账户、taker/maker 费率、skip_nonce)以贴近真实 integrator 场景。
  • 组件微基准(micros.rs):通过decode_onlyparse_only把 JSON 解码与域解析拆开;通过atom/*系列测量Decimal::from_strPrice::from_decimal_dpQuantity::from_decimal_dpTradeId::newUUID4::new等原子成本;签名侧则拆出compute_tx_hashhash_to_quintic_extensionhash_two_to_quinticmulgen_ct(生成元上的常数时间标量乘法,是 Schnorr 签名的主导成本)、sign_txrender_create_order_json。其中wire_wrap_valuewire_wrap_raw一对对比了旧路径(serde_json::from_strValue构建 AST)与新路径(Box<RawValue>仅校验字节不建 AST)的节省,可以直接佐证一次具体的重构收益。

七、Poseidon2 专项基准(signing_poseidon2.rs

signing_poseidon2.rs对 Poseidon2 置换与海绵结构做专项测量:hash_no_pad在输入长度[1, RATE, 2*RATE, 3*RATE]上扫描,使吸收循环或置换本身的回归呈现为不同的曲线;permute则单独计时(见 signing_poseidon2.rs)。该套件与signing_field_iai.rs的指令数门槛共同构成签名原语层的回归防线。

八、结语:一套可复现、可引用、可追溯的基准体系

Lighter 适配器的基准体系遵循 nautilus_trader 全仓库的 BENCHMARKING.md 政策,做到了三层分工:signing_sign_verify.rs是面向外部的签名基线;data.rs/exec.rs覆盖入站与出站两条管道;micros.rssigning_field_iai.rs提供层级化的定位手段。而 Rust 侧约 3.2x–4.2x 于官方 Go 栈的加速比,是在固定 nonce、同源 fixture、同机同控制条件下得到的可复现结论——引用任何一行数字前,请先确认复现环境与口径(bench-ltoprofile、performance调频、setarch -R关闭 ASLR),并遵守"只引用signing_sign_verify.rs、不把micros.rs重复项当基线"的纪律。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 22:13:11

VTK观察者模式与事件回调机制详解

1. VTK回调事件与观察者模式解析 在可视化工具包VTK的开发中&#xff0c;事件回调机制是实现交互功能的核心基础设施。我第一次接触这个机制是在开发医学影像测量工具时&#xff0c;需要实时获取鼠标在三维视图中的位置坐标。传统的事件处理方式无法满足这种持续性的交互需求&a…

作者头像 李华
网站建设 2026/9/10 22:12:07

10 分钟把沉浸式翻译接上本地模型,断网也能双语翻译

10 分钟把沉浸式翻译接上本地模型&#xff0c;断网也能双语翻译 【免费下载链接】immersive-translate 沉浸式双语网页翻译扩展 , 支持输入框翻译&#xff0c; 鼠标悬停翻译&#xff0c; PDF, Epub, 字幕文件, TXT 文件翻译 - Immersive Dual Web Page Translation Extension …

作者头像 李华
网站建设 2026/9/10 22:10:51

怀化AI短视频怎么制作?小白也能轻松上手

来源&#xff1a;唐sirAI&#xff08;www.tangsir.cc&#xff09; | 电话&#xff1a;18874530691━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━很多怀化的商家在搜索怀化AI短视频怎么制作时&#xff0c;都会有各种各样的疑问。今天&…

作者头像 李华
网站建设 2026/9/10 22:08:46

CANN/GE模型查询销毁函数

aclmdlBundleDestroyQueryInfo 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTor…

作者头像 李华