一、行情系统概述:高频交易对行情系统的要求
做高频交易的人都知道,行情系统是整个交易链条的「眼睛」。眼睛要是慢了,手再快也没用。我入行那会儿,带我的老工程师说过一句话,我一直记着:「行情延迟每多一微秒,你的策略就少一分胜算。」
高频交易对行情系统的要求,说白了就三个字:快、准、稳。但真要落地,每个字背后都是一堆坑。
1.1 极低延迟:微秒级的生死线
高频交易里,延迟不是「越快越好」,而是「必须低于某个阈值」。我个人习惯把延迟拆成三部分来看:
- 网络传输延迟:从交易所撮合引擎到你的网关,光纤路径、交换机跳数,每多一米都是成本
- 协议解析延迟:TCP/IP 协议栈、应用层解码,这里最容易出「隐形延迟」
- 数据分发延迟:行情数据从网卡到策略进程,中间经过几层拷贝?
我在项目中遇到过最典型的情况:某团队用软件方案做行情解析,延迟稳定在 5 微秒左右。后来换了 FPGA 硬解析,直接降到 800 纳秒。你想想看,差了整整 6 倍多。这不是优化,这是降维打击。
核心指标参考:
| 场景 | 可接受延迟 | 理想延迟 |
|---|---|---|
| 普通量化交易 | < 100 μs | < 50 μs |
| 高频做市 | < 10 μs | < 5 μs |
| 极高频套利 | < 1 μs | < 500 ns |
1.2 高吞吐量:别让数据堵在路上
有人觉得「延迟低 = 吞吐高」,其实不是。我见过不少系统,延迟测出来漂亮,一到行情爆发期就崩了。为什么?因为行情数据是突发性的——平时每秒几千笔,开盘瞬间可能冲到几十万笔。
吞吐量的瓶颈通常出现在三个地方:
- 网卡接收队列:中断太多,CPU 被频繁打断
- 内存带宽:数据拷贝次数多,DDR 带宽被吃满
- 处理核心:单核处理不过来,多核之间又有锁竞争
我曾经踩过一个坑:某次行情系统上线前压测,单路行情流没问题,但同时接入沪深两所数据时,网卡直接丢包。查了半天,发现是驱动参数没调,接收描述符队列深度不够。嗯,这种问题,软件层面调一调就能解决,但如果你不懂硬件原理,连问题在哪都找不到。
1.3 数据完整性:丢了数据等于丢了钱
高频交易里,丢一笔行情可能意味着策略做出错误决策。我见过最惨的案例:某团队因为行情网关丢了一个档位的深度数据,策略以为价格没变,结果实际已经跳了 10 个 tick,直接造成几十万的亏损。
保证完整性的手段,我建议至少做到三层:
- 硬件层:网卡支持硬件校验和,TCP 卸载引擎
- 协议层:应用层序列号校验,乱序重排
- 应用层:多路冗余接收,交叉校验
注意:不要完全依赖 TCP 的可靠性。在极端网络拥塞下,TCP 重传可能已经过了你的交易窗口。我建议在应用层做心跳和序列号校验,超时未到直接切换备用链路。
二、软硬协同设计的核心理念
软硬协同设计,这个词听起来高大上,其实核心就一句话:把能硬化的都硬化,剩下的交给软件。
为什么要这么做?因为 CPU 处理网络数据包,本质上是在「模拟」硬件该做的事。你想想看,一个网络包从网卡到应用层,要经过中断处理、协议栈解析、内存拷贝、上下文切换……每一步都在消耗 CPU 周期。而这些工作,很多是固定模式的,完全可以用硬件流水线来做。
2.1 什么该硬化,什么该留软?
我个人习惯用「频率 × 确定性」这个矩阵来判断:
| 任务类型 | 频率 | 确定性要求 | 推荐方案 |
|---|---|---|---|
| 网络协议解析 | 极高 | 极高 | FPGA 硬解析 |
| 行情数据重组 | 高 | 高 | FPGA 或 GPU |
| 策略信号计算 | 中 | 中 | CPU/GPU 软件 |
| 风控逻辑 | 低 | 极高 | CPU 软件 + 硬件看门狗 |
举个例子:UDP 组播行情解析。软件做的话,每收到一个包就要做一次协议解析、一次内存拷贝、一次应用层回调。FPGA 做的话,数据从网口进来,直接在硬件流水线里完成 MAC 层过滤、IP 层校验、UDP 端口匹配,最后把净荷直接 DMA 到共享内存。延迟从微秒级降到纳秒级,而且抖动极小。
我的经验:不要试图把所有逻辑都塞进 FPGA。FPGA 开发周期长、调试困难,适合做那些「不变且高频」的任务。策略逻辑这种经常变的东西,还是留给 CPU 吧。我曾经见过一个团队把策略也写进 FPGA,结果每次改参数都要重新综合布线,折腾了三个月,最后老老实实改回 CPU 实现。
2.2 软硬协同的分层架构
我习惯把行情系统分成四层:
- 物理层:网卡、光模块、FPGA 加速卡
- 解析层:协议解码、数据重组、校验
- 分发层:共享内存、零拷贝、多路广播
- 应用层:策略计算、风控、日志
每一层之间用「契约」来约定接口。比如解析层输出的是固定格式的行情结构体,分发层只管把结构体送到指定内存地址,应用层直接读取。这样各层可以独立演进,硬件升级不影响上层逻辑。
下面这张图是我常用的架构示意:
三、延迟与吞吐量的权衡
做系统设计,最怕的就是「既要又要」。延迟和吞吐量,很多时候是矛盾的。你想想看,要降低延迟,最好让数据走最短路径,少排队、少缓存。但要提高吞吐量,又需要足够的缓冲来应对突发流量。
怎么权衡?我总结了三个原则:
3.1 区分「关键路径」和「非关键路径」
不是所有数据都需要极低延迟。比如行情快照和逐笔成交,快照是定时发送的,延迟要求没那么高;逐笔成交是实时推送的,必须优先处理。
我建议的做法是:
- 关键路径(逐笔成交、最优报价):走硬件加速,零拷贝,独占 CPU 核心
- 非关键路径(快照、统计信息):走软件处理,可以适当缓存,批量处理
这样既保证了核心数据的低延迟,又不会因为处理所有数据而把系统压垮。
3.2 用「流水线」代替「批处理」
很多人习惯把数据攒一批再处理,觉得这样吞吐高。但在高频交易里,这种做法会引入额外的延迟抖动。我更喜欢用流水线:每个处理阶段只做一件事,做完立刻交给下一级。
举个例子:
// 批处理方式(不推荐)
while (1) {
batch = collect_packets(100); // 攒够100个包
process_batch(batch); // 批量处理
}
// 流水线方式(推荐)
while (1) {
pkt = recv_packet(); // 收一个包
parse(pkt); // 立刻解析
dispatch(pkt); // 立刻分发
}
流水线方式下,单个包的延迟是固定的,不会因为等待批量而引入额外延迟。吞吐量可能略低,但延迟的确定性好得多。
关键权衡点:
- 追求极致低延迟 → 流水线 + 硬件加速 + 独占资源
- 追求高吞吐量 → 批处理 + 多核并行 + 大缓冲区
- 实际工程中 → 混合模式,关键路径用流水线,非关键路径用批处理
3.3 实测数据比理论更重要
我见过太多团队,设计阶段算得天花乱坠,一上线就露馅。为什么?因为理论模型忽略了现实中的各种干扰:CPU 缓存 miss、内存带宽争抢、网卡中断亲和性没配好……
我的习惯是:
- 先搭最小原型:用最简单的方案跑通全链路
- 打点测量:在每个关键节点埋时间戳,精确到纳秒
- 找瓶颈:看哪一段延迟占比最大,优先优化
- 迭代:改一处,测一次,不要一次改太多
我曾经优化一个行情网关,理论分析觉得瓶颈在协议解析,结果实测发现 70% 的延迟花在了内存拷贝上。后来改成共享内存 + 零拷贝,延迟直接降了 60%。所以说,没有实测数据,一切优化都是瞎猜。
避坑指南:我曾经犯过一个错误——在开发环境测出来的延迟很漂亮,但生产环境一跑就翻倍。后来发现是开发环境的网卡中断亲和性没配好,所有中断都落在同一个 CPU 核心上。生产环境流量大,那个核心被打满了,延迟自然就上去了。所以,压测一定要用生产级别的流量,而且要多场景测试。
好了,行情系统的核心要求、软硬协同的理念、延迟与吞吐量的权衡,大概就是这些。这些东西说起来简单,真正落地的时候,每个点都能挖出不少坑。后面我们会一步步深入,把每个环节都拆开来看。