如果一家交易系统像一间忙碌的餐厅,传统 Actor 模型要求每位员工只管自己的工作台,所有请求都写成纸条,投入对方的信箱,再等对方抽空处理。这样不容易互相添乱,却要为递纸条、排队和换人付出时间。对普通软件,这点开销未必重要;对高频交易,几微秒也可能挤满预算。
Vincent Maciejewski 的论文提出:当两个 Actor 就在同一台机器、同一个进程里时,不妨允许一次受约束的“当面递话”。发送方直接执行接收方的处理逻辑,当场取得回复,同时保留 Actor 对状态的隔离。这套机制名为 fast_send,实现于开源 C++20 框架 kaspar-hft。论文还用 CME 实时行情测试它,但目前所有设计与效果论断都来自作者这一单一信源,尚缺独立复现。
Actor 为什么曾被挡在门外
Actor 模型是一种组织并发程序的方法:程序被拆成许多独立“角色”,每个 Actor 只管理自己的状态,通过消息沟通,并且一次只处理一条消息。这样可以减少多个线程同时修改同一份数据造成的数据竞争,也让工程师不必手工安排大量锁。
代价同样来自消息。传统异步投递中,发送方先把消息放进接收方的 mailbox——也就是待办队列,然后继续做自己的事。消息通常需要在堆上分配内存,还要经历入队、出队、调度和线程切换。请求与回复各走一遍,成本再翻一轮。
高频交易(HFT)依靠高速行情、自动决策和快速下单,在极短时间内完成反应。这里讨论的不是策略赚不赚钱,而是软件架构能否守住微秒级响应预算。Actor 带来的隔离很有吸引力,但传统递信方式显得太慢,这正是论文要化解的矛盾。
把排队改成一次受控的直达
fast_send 是整篇论文最关键的一步。发送线程取得接收 Actor 的独占访问权,直接在自己的线程里运行对方的 handler——即收到某类消息后执行的处理函数,并把回复作为返回值带走。同步请求还可以放在调用栈上,不必为等待稍后处理而单独分配堆内存。
它并不是随意调用另一个对象的方法。接收 Actor 运行 handler 时仍受独占访问保护,同一时刻不会被其他线程同时修改。更重要的是,论文强调 receiver transparency,也就是“接收方透明”:handler 使用同一套写法,无法判断消息是同步还是异步送达,也不知道自己正在哪个线程上运行,更不知道发送者是否在等待。
这层透明性很关键。若接收方必须针对快速路径另写一套逻辑,系统就会重新分裂成“安全但慢”和“快但难管”两套代码。fast_send 想做的是更换投递路线,而不是拆掉 Actor 的边界。
不过,同步调用会带来新的风险。假设 Actor A 等待 B,B 又同步调用 A,程序可能形成循环,最终死锁或不断递归。论文的处理办法是在每个线程保存一条调用链,并在获取任何锁之前检查目标是否已经出现。作者还提出按“Actor 与消息类别”细分的宽松版本,以容纳部分回调模式。
fast_send 也不是普通异步消息的加速拼写。它不会排在 mailbox 里已有消息之后,而是在取得接收方的锁后优先执行。因此,需要与其他发送者严格保持先进先出顺序时,仍要使用普通 send。
不只改一条发送路径
kaspar-hft 总共做了四项调整。
第一项是 fast_send。第二项是 actor groups:把一组 Actor 安排在同一个线程上,共用一个 mailbox。若一条处理链里的 Actor 都在组内,同步调用可以一路在最初的线程上完成。论文把这项架构变化概括为:由异步链上随 Actor 数量增长的 次调度上下文切换,降至 。这说的是调度次数,不等于整体耗时也获得同等量级的改善。
第三项是允许每个 Actor 选择适合其写入模式的 mailbox queue。单一突发生产者、许多发送者同时写入,以及共享队列,面对的竞争方式不同,作者认为不该强迫它们使用同一种队列。
第四项是 memory pool——预先管理并重复利用一批内存,避免每条异步消息都向通用分配器申请空间。论文称,这不仅减少通常开销,也用于避开内存分配可能带来的毫秒级长尾。
五种投递操作则覆盖从普通异步、同步等待,到失败后退回异步等不同选择,但接收方仍通过统一 handler 处理。换句话说,发送方选择速度与稳健性的取舍,接收方不必跟着改代码。
实盘数据说明了什么
作者先做微基准测试。论文称,同步往返延迟达到“几十纳秒”,约比未分组的异步路径低两个数量级。不过,这个说法没有随材料给出具体数值、延迟分位数、硬件、编译参数或完整测试方法,只能视为初步结果,不能当成普遍性能保证。
更贴近交易现场的证据来自 CME 实时行情数据流,覆盖 ES、NQ 和 ZN 期货。测试观察 socket-to-book latency,也就是行情从网络套接字进入系统,到本地订单簿完成更新所需的时间。论文把其中约 7 微秒归为解码与更新订单簿的基础成本,随后每多一条消息,还会增加相应的处理斜率。
按照作者的测量,kaspar-hft 自身的延迟贡献不到这约 7 微秒基础成本的 1%。这里不能简写成“框架占端到端延迟不到 1%”:它的分母只是论文划定的 decode-and-book floor,测量边界、计时方法、样本量与尾延迟分位数仍需核查。
作者还认为,延迟长尾主要由市场数据的到达方式造成。行情不是像节拍器一样均匀抵达,而会成簇爆发;一批消息进入同一数据包后,需要依次解码,罕见突发还可能让 mailbox 队列积压。论文因此把长尾归因于市场的非泊松、强聚集到达过程,而非 Actor 机制。不过,材料没有提供独立数据或完整对照实验,这项归因还指向一篇未提供的配套论文,结论宜谨慎看待。
真正值得看的,是同一份代码
性能之外,shared-queue group 还带来一个交易系统很看重的性质:同一套 Actor 代码可以用于实盘和 deterministic backtest——确定性回测。实盘时,队列接收现场行情;回测时,系统按记录顺序重放消息。只要 Actor 的行为完全由收到的消息决定,相同输入就应产生相同状态和输出。
论文把这种“生产与模拟二合一”列为重要收益。它意味着策略无需为回测另写一套核心逻辑,也更容易重放触发故障的消息序列。不过,“代码无需修改”仍是作者表述;材料没有说明是否还要更换数据适配器或配置,也没有交代实际部署的范围。
这项工作的意义,不只是把 Actor 跑快一点。它换了一个角度:Actor 的安全纪律与异步投递的全部成本未必必须捆绑。对同机 Actor,可以保留状态隔离和逐条处理,同时让关键请求走同步直达;真正需要跨线程或远程通信时,再回到异步 mailbox。
局限与未知
- 论文只测试了 CME 的 ES、NQ、ZN 期货,不能直接外推到其他交易所、资产、硬件或负载。
- 饱和吞吐量与高竞争场景被留作未来工作;现有结果不足以说明系统在持续满载时的表现。
- 所有实现与测量结论均来自论文作者及其项目,尚无独立复现;尤其是纳秒级微基准、尾延迟归因和“无需修改即可回测”的边界,仍需更多证据。