1. 硬件交易系统概述:从软件到硬件的演进

1.1 为什么我们需要聊这个?

做低延迟交易系统,说白了就是跟时间赛跑。

我入行那会儿,大家还在用软件做订单簿。一台高性能服务器,配上C++写的核心逻辑,跑个微秒级延迟,已经算很牛了。但后来我发现,软件再怎么优化,终究有天花板——操作系统调度、CPU中断、缓存未命中,这些开销你躲不掉。

于是,硬件开始登场。

你想想看,FPGA上跑的逻辑,没有操作系统,没有进程切换,没有缓存污染。数据从网口进来,到处理完发出去,走的是一条纯硬件流水线。这个延迟,是纳秒级的。

嗯,这就是我们做这门课的初衷。

1.2 从软件到硬件的演进

我简单梳理一下这个演进过程,你感受下:

阶段 实现方式 典型延迟 我踩过的坑
纯软件 C++ 用户态程序 10-50 μs GC暂停、内核调度抖动
内核旁路 DPDK、Solarflare 1-5 μs CPU亲和性没配好,反而更慢
FPGA加速 Verilog/VHDL 硬件流水线 100-500 ns 时序收敛搞了我两周
全硬件订单簿 FPGA片上BRAM + 并行比较器 < 100 ns BRAM不够用,被迫做分层存储

你看,从微秒到纳秒,差了整整两个数量级。在交易领域,这可不是小数目。我曾经帮一个客户做优化,把订单簿从软件搬到FPGA上,延迟从12微秒降到了80纳秒。客户当时就说了句:「你们这玩意儿,值。」

1.3 为什么需要硬件订单簿?

这个问题其实很直接——软件订单簿的瓶颈,你绕不过去。

我举个例子。一个典型的软件订单簿,维护一个价格-数量映射表。来一个订单,你要做这几件事:

  1. 解析网络包(中断 + 拷贝)
  2. 查找价格档位(红黑树 / 跳表)
  3. 更新数量(内存写)
  4. 生成行情快照(序列化)
  5. 发送出去(系统调用)

每一步都有开销。尤其是查找和更新,在订单量大的时候,CPU缓存命中率直线下降。我见过一个极端情况——某交易所行情爆发时,软件订单簿的延迟从5微秒直接飙到200微秒。这谁受得了?

硬件订单簿就不一样了。FPGA上,价格档位存在BRAM里,比较器阵列并行工作。来一个订单,所有价格档位同时比较,一个时钟周期就能找到匹配的档位。更新也是纯硬件操作,没有锁,没有竞争。

核心差异一句话:软件是串行查找,硬件是并行比较。并行,就是快。

1.4 低延迟交易的核心挑战

做低延迟系统,说白了就三个字:确定性。

延迟低还不够,你得保证每一次的延迟都低。不能这次80纳秒,下次800纳秒。交易系统最怕的就是抖动。

我总结一下核心挑战:

  • 时序收敛:FPGA跑在几百兆赫兹,一个路径没收敛,整个设计就废了。我曾经为了一个跨时钟域的问题,调了整整三天。
  • 资源限制:FPGA的BRAM、DSP、LUT都是有限的。订单簿深度越大,资源消耗越大。你得学会做取舍。
  • 流水线深度:硬件流水线越长,延迟越大。但流水线太短,又跑不高频率。这是个平衡艺术。
  • 数据一致性:多个订单同时到达,你怎么保证状态不乱?硬件里没有锁,全靠设计保证。

注意:千万不要以为FPGA就是「把C代码翻译成Verilog」。我见过太多人这么干,结果跑出来的延迟比软件还差。硬件思维和软件思维,完全是两码事。

1.5 硬件订单簿的整体架构

下面这张图,是我个人习惯用的硬件订单簿顶层架构。你看一眼,心里就有数了:

硬件订单簿处理引擎 - 顶层架构 网络接口 10G/25G/100G 协议解析 UDP/TCP 解析 订单簿核心 BRAM + 比较器阵列 并行价格匹配 行情生成 快照/增量 各模块关键指标: • 网络接口:支持10G/25G/100G以太网,MAC层直接接入FPGA • 协议解析:硬件解析UDP/TCP头部,提取订单字段,延迟 < 20 ns • 订单簿核心:BRAM存储价格档位,比较器阵列并行匹配,延迟 < 50 ns • 行情生成:支持逐笔行情和快照行情,输出格式可配置 设计要点: 1. 流水线深度控制在 8-12 级,平衡频率和延迟 2. 所有模块使用独立时钟域,通过异步FIFO隔离 3. 关键路径做寄存器复制,减少扇出

我的经验:刚开始做硬件订单簿时,我总想把所有功能塞到一个模块里。结果时序一塌糊涂。后来学乖了,把协议解析、订单簿核心、行情生成拆成三个独立流水线,中间用FIFO缓冲。这样每个模块的时序都好收敛,调试也方便。

1.6 一个简单的例子:价格档位查找

我拿价格档位查找举个例子,你就明白硬件和软件的区别了。

软件里,你可能会这么写:

// 软件:红黑树查找
auto it = price_map.find(order_price);
if (it != price_map.end()) {
    it->second.quantity += order_qty;
}

这段代码,平均查找复杂度是 O(log n)。n 是价格档位数。如果市场上有1000个价格档位,大约需要10次比较。每次比较还有分支预测、缓存访问的开销。

硬件里,我们这么干:

// 硬件:并行比较器
always @(posedge clk) begin
    for (i = 0; i < NUM_LEVELS; i = i + 1) begin
        if (price[i] == order_price) begin
            quantity[i] <= quantity[i] + order_qty;
        end
    end
end

注意看,这个 for 循环在硬件里是并行展开的。所有价格档位同时比较,一个时钟周期就出结果。不管你有10个档位还是1000个档位,延迟都一样。

关键点:硬件里,并行是免费的。只要你资源够,想并多少就并多少。这就是硬件订单簿能跑到纳秒级的根本原因。

1.7 避坑指南

最后,分享几个我踩过的坑:

  • 别迷信「全硬件」:不是所有逻辑都适合搬到FPGA上。复杂的业务逻辑、频繁变更的规则,还是留给软件处理。硬件只做最核心、最稳定的部分。
  • 重视仿真:我吃过一次大亏——仿真跑得好好的,上板就挂。后来发现是复位逻辑没处理好。硬件调试比软件麻烦得多,仿真能帮你省下大量时间。
  • 预留调试接口:FPGA内部的状态,你总得有个办法看。我习惯在每个模块都加一个AXI-Lite寄存器接口,方便上位机读取内部状态。

血的教训:我曾经在一个项目里,为了省几个LUT,把调试接口全删了。结果上板后订单簿行为异常,我花了整整一周才定位到问题——一个跨时钟域的同步器少打了一拍。从那以后,我再也不敢省调试接口了。

好了,这一章就聊到这儿。硬件订单簿的世界,才刚刚打开。后面的章节,我们会一步步深入,从协议解析到价格匹配,从行情生成到性能调优。你准备好了吗?


公众号:蓝海资料掘金营,微信deep3321