想象超市只有一名店员:扫码、装袋、收款都由他完成。客人零散到来时,这样最省事;但十几个人突然一起进门,队伍就会迅速拉长。此时增加分工,虽然每位顾客都要多走一个柜台,最后几个人却可能更早离开。
高频交易系统也面对类似选择。传统做法是把读取行情、更新订单簿、运行策略和生成订单放在同一个线程里依次完成。理由很直接:把任务交给另一线程,会增加入队、缓存传递和调度等开销。Vincent Maciejewski 的一篇 arXiv 预印本却提出,这条经验只在系统足够快时成立;一旦处理速度跨过行情源的特定门槛,多线程流水线增加的一次固定成本,可能换来更短的尾部延迟。
这里的“尾部延迟”,指少数最慢消息经历的等待时间。交易系统不能只看典型消息有多快,因为真正危险的往往正是拥堵时落在队尾的消息。以下数字与结论均来自这篇尚未经过同行评议的单一预印本。
先别数线程,先看消息怎么来
作者分析了超过一年的 CME 市场数据,研究对象是 NQ 近月合约,规模达到数十亿个数据包及相近数量的撮合引擎事务;还用其他品种上的实时生产接收器核对部分特征。
论文追踪了三段过程。撮合引擎先把订单处理成交易事务;行情发布器再把结果编码为网络数据包;接收器最后解码数据并更新订单簿。数据携带撮合引擎和发布器的时间戳,接收端再加入自己的时间戳,因此作者可以逐包观察消息如何穿过整条链路。
关键发现不是“包很多”,而是“包成簇到来”。论文称其接近临界状态的自激簇集:一次事件发生后,短时间内出现后续事件的概率会上升。换句话说,平均每秒收到多少包并不能完整描述压力。平均客流看似宽松,门口仍可能突然涌进一群人。
作者进一步把数据包还原到撮合引擎事务,认为这种簇集主要属于交易事务本身,而不是交易所如何封装数据。几乎每个事务都能放进一个包。撮合引擎经常在不足一微秒内连续处理多个事务;但论文观察到的行情发布器最多约每 7.5 微秒发送一个包。于是,引擎端几乎同时发生的事务,会在接收端表现为间隔约一个发布周期的包列。
这一区分很重要。一个业务事务和一个网络包并非天然一一对应。如果只盯包速率或包大小,工程师可能会找错拥堵来源。论文的解释是:当接收器出现排队时,主因是交易事务的时序,而非包变大或发布器随意打包。
7.5微秒是一条分界线
如果接收器能在约 7.5 微秒的发布周期内处理完每个包,下一个包到达前,当前工作已经结束。按照论文的模型,无论市场事务怎样成簇,包到达本身都不会形成接收端队列。此时单线程最好:拆成多个阶段只会平白增加一次或多次线程交接成本。论文研究的生产接收器就在略低于这一周期的区间运行。
问题出现在单包处理时间超过发布周期之后。包列中的前一个任务还没做完,后一个包已经抵达,等待会不断累积。论文在真实到达序列的模拟中发现,尾部延迟增长得比单次处理时间快得多;用相同包数、但到达时间服从 Poisson 分布的对照流量,则没有同样强的长尾。Poisson 分布可理解为事件相互独立、不会彼此“招来”后续事件的随机到达。
这时,把原来的一条服务链拆成两个线程上的两个阶段,结论可能反转。每条消息都要承担一次线程跳转,因此中位数延迟会上升;但两个阶段可以并行处理不同消息,更快排空突发队列,从而消除论文所称的“大部分”长尾。
所以,“线程切换未必更慢”不是说切换免费,也不是说线程越多越好。更准确的说法是:当固定的线程交接成本,小于流水线消掉的排队等待时,牺牲一点典型延迟可能值得。
真正该切的是瓶颈
论文给出的工程原则很克制:是否拆分,只看拆分能否缩短最慢阶段。
假设一条处理链由解码、订单簿更新、策略计算和订单构造组成。把它拆成多个线程,并不会减少每条消息的总工作量。流水线的吞吐能力取决于其中耗时最长的阶段。若两个阶段分别需要很短和很长的时间,前者再快也救不了后者形成的队列。
作者用一个精确归约来表达这一点:在各阶段服务时间固定的串联系统中,整条流水线的排队行为可以归约为一个服务时间等于最慢阶段的单服务器,再加上线程交接的固定成本。论文还提出 burst-limit throughput identity,用来描述孤立突发中流水线的清队速度。直观上,阶段均衡时,不同线程能够同时处理不同消息;阶段失衡时,所有消息仍会堵在最慢的一站。
对于不保存状态的阶段,论文称把完整数据包分发给一组核心,效果至少不差于两阶段拆分。订单簿这类有状态阶段则不能随意分发,因为连续消息需要在同一份状态上按顺序处理。
对工程团队有什么用
这项工作的价值,不在于替多线程“翻案”,而在于把架构选择改写成一个可测量的问题。
第一,测出具体行情源的发布周期,并尽量让关键处理链低于它。第二,如果做不到,再寻找能真正缩短最慢阶段的切分点。第三,不要只比较平均值或中位数,还要比较加入线程交接成本后的端到端长尾。
对于已经略快于发布周期的生产接收器,论文观察到的残余长尾主要来自两件事:少数数据包含有多条消息,需要逐条解码;不同数据包的服务时间并不恒定。此时优化重点不是增加线程,而是降低每条消息的处理成本,并压缩服务时间的波动。
这也解释了标题里的反常识:单线程没有失效,它只是有适用区间。真正决定答案的,是数据如何成簇到达、处理时间位于发布周期的哪一侧,以及切分后最慢阶段是否真的变短。
局限与未知
- 全部结果来自同一篇 arXiv 预印本,尚无独立机构复测或同行评议。数据包抓取也不能公开再分发。
- 约 7.5 微秒是论文在 CME NQ 数据上观察到的发布周期,不能当作所有 CME 产品、行情通道或其他交易所的固定参数。
- 材料没有给出关键改善比例、完整延迟分位数、硬件配置和线程亲和性设置;“消除大部分长尾”等强表述目前只能按作者结论理解。论文也没有回答订单为何会在撮合引擎端如此密集地到达。