Exablaze Exanic X25
Exanic X25 是目前金融交易领域常见的一款 FPGA 加速网卡,通常被用在行情接收侧用极致的速度接收来自交易所的行情组播(交易侧几乎已经被 Solarflare 一统天下了)。
X25 其实还有一个名字,K3PS (Cisco Nexus K3P-S FPGA SmartNIC),Exablaze 是澳洲一家专门做 HFT 网络设备的公司,后面被 Cisco 收购了,Cisco 收购之后就给 X25 改了个新名字叫 K3PS, 甚至现在卡上面没有任何一个地方印了 X25 的字样,不过大家仍然习惯叫它 X25~
虽然 Exablaze 网卡线被收购之后就有点摆烂,很久没有新产品和新 feature 了,但是 X25 仍然是一款非常优秀的遗产!
X25 的核心卖点就是,极致的低延迟,双向穿透延迟只要 568ns!
普通的网卡双向穿透延迟大概是在几十微秒级别,而普通的低延迟卡,通常也只能做到 1-2us 量级;即使是 X25 最强有力的竞争对手,X4522 也需要 590ns,上一代的 X2522 则更慢一点需要 750ns。
除了 568 ns 的穿透延迟,官方介绍的核心卖点还有这些:
- FDK 可编程(Solarflare X2 X4 系列卡就是纯 ASIC 不能编程)
Cut-through 流式 RX
TX 模板预加载
exasock socket 库(当然其实这个算是个减分项。。。)
4ns 精度硬件时间戳
总的来说,是一款既性能极致又功能丰富的网卡。

网卡收包路径
从光信号 -> 用户态字节,X25 的收包路径大概是:
- 光信号
- SFP28 模块光电转换,把光信号转化为电信号
- 收发器 CDR 恢复时钟、解串成 66b 字
- PCS 块同步 66b 解码
- MAC 分帧
- (optional)用户自定义 FPGA 开发逻辑处理
- flow steering 选环
- 攒够 120B 数据 or 帧结束
- PCIe 直接把 128B 数据写入 Host 内存 2MB Ring (卡上不缓冲、无流控)
- exanic_receive_chunk 用户态轮询到数据
- 返回给用户态程序自行读取处理

相较于常规网卡,X25 收包不一样的部分在于,首先 X25 支持 Cut-through 流式,阶段 8 的时候,假设收到一个 1200B 的以太网帧,常规的网卡是 Store-and-Forward 模式,需要等这个 1200B 全部处理完,然后才能发起 DMA 写入,而 X25 收到前 120 字节之后就可以发出,流式读取,一边网卡上还在收着帧呢,一边用户态就已经可以开始处理了,可以极大减少网络包前部的延迟;不过代价是用户可能全部处理完之后才发现 CRC 校验没过~
另外第 9 阶段,常规网卡通常是在用户态会有一个 RX Descriptor Ring,每次网卡需要先取一些 descriptor,然后再写入到 descriptor 指向的内存,写完还得回写 descriptor 的状态,然后通过中断通知 CPU 写完了,descriptor 存在的意义本质上是“内存归软件管”,网卡只有使用权,网卡需要为这套灵活性的机制付出一些性能税。
而 X25 则只有一个简单的 RX Ring,非常非常粗暴,ring 固定 2MB、chunk 固定 128B、地址由 base 加计数器直接算出, 也没有节流机制,网卡就粗暴的来了包就 Cut-Through 搬过去,溢出了就直接覆盖了,用户态进程要是消费不过来,那是你自己问题,反思一下是不是消费的不够快,是不是哪里有 bug。
甚至 X25 也不支持中断 or 通知机制,你只能一直轮询那个 ring,一直不断地轮询,这样 CPU 开销会非常巨大,不过会用这种卡的用户为了延迟通常是不会吝啬一个 isolated CPU 核心的。
网卡发包路径
从用户态发出去 -> 网卡设备光信号进入光纤,流程如下:
- 调用用户态 API exanic_transmit_frame
- 封装 tx_chunk 通过 mmap 的 write-combining 映射写入卡上
- CPU 每 64B 一个 PCIe 写入网卡上的 buffer,最后 flush WC 清除残留
- doorbell 写 TX 寄存器,写入帧起始 offset 通知网卡开始工作
- MAC 封帧
- PCS 64b/66b 编码、加扰
- 收发器并串转换
- SFP28 电 -> 光
- 卡把 feedback_id 回写到主机 feedback slot,通知发送完成

发包和常规网卡本质区别在于,常规网卡是 pull 模式发包,而 X25 是 push 模式,这个速度差距应该就不言而喻了。
常规模式网卡是先要把要发送的完整报文在拼装好,放到内存里面,可能是 kernel 封的,也有可能是一些用户态的库封的,anyway 封好之后,申请 TX Descriptor 然后 doorbell 通知网卡,网卡再通过 DMA 读取对应的内存地址数据,后面的流程就基本都一样了,MAC、线路编码、并串转换、变光。
X25 之所以使用 TX 网卡上的 TX Buffer,而不是 CTPIO 路线,其实它是赌一个场景:本身这个卡就是为了金融高频交易领域设计的,用户拿它的卡出方向无非就是报单,他就笃定报单的时候,报文是一个非常固定的模板,每次可能只要改动价格、数量、合约这几个少数的字段,然后直接 doorbell 即可,而其他大部数字段都是不需要改变的。
所以它在 TX Buffer 的基础上面做了一招“预加载”:用户可以先把要发送的报文模板预先写到 TX Buffer 里面,等 PCIe 过来的时候,就只修改几个特定位置的数据然后直接发送,能降低关键路径上发送完整报文的部分开销。
用户态 API
exanic sock 用起来也很简单,真正重要的函数只有三个:
exanic_receive_chunkexanic_receive_frameexanic_transmit_frame
收包的话,就是用这两个 recv API,receive_chunk 就是单独接收一个 120B 的 cut-through 网络包块,recv_frame 就是接收一个完整的帧,跟别的 socket API 用起来更像,但是底层其实就是循环调用 recv_chunk 然后把这些 chunk 拼成一个完整的帧,算是前者的一个包装函数。
发包的话,就是常规发送的 API,但是如果要用模板发送功能的话,就要拆成两个 API
- exanic_begin_transmit_frame
- exanic_end_transmit_frame
有点类似数据事务,在中间这个期间这个 handle 是不能发别的帧的,通常来说,要结合业务场景去利用这个功能。
比如说金融领域的报单场景,通常每一次报完单之后,就可以立刻调用 begin_frame 然后 mmap 把模板准备好,等下一次订单到来的时候,直接 mmap 修改关键参数,然后调用 end_frame 发出即可。
参数对比
跟类似竞品的参数对比表格我整理了一份,如下:
| X25 | X10 | X2522 | X4522 | U50 | |
|---|---|---|---|---|---|
| 上市时间 | 2020-01 | 2016-02 | 2018 | 2025-10 | 2019-08 |
| 厂商 / 血统 | Exablaze → Cisco(Nexus K3P-S) | Exablaze → Cisco(Nexus K35-S) | Solarflare → Xilinx → AMD | AMD Solarflare | Xilinx → AMD Alveo |
| 定位 | 低延迟 NIC,FPGA 可编程 | 低延迟 NIC,FPGA 可编程 | ASIC 低延迟 NIC,kernel bypass | ASIC 低延迟 NIC,Express / Enterprise 双数据通道 | 通用 FPGA 加速卡,自己开发固件 |
| 穿透延迟¹ | 568ns 最低 / 696ns 中位,raw 帧 | 780ns 中位,raw 帧 | 750ns ½RTT @25G / 819ns @10G,ef_vi 32B | 590ns ½RTT @10G,ef_vi 4B(X4542 数据,同代) | vendor 估算 T2T <0.5µs,无可比口径 |
| FPGA / 芯片 | Xilinx Kintex UltraScale+ XCKU3P-2 | Xilinx Kintex UltraScale XCKU035-2 | Solarflare SFC9250 ASIC | AMD NS9480 ASIC | Xilinx UltraScale+ XCU50(2 SLR) |
| 端口 | 2× SFP28,25G / 10G / 1G / 100M(25G 靠固件) | 2× SFP+,10G / 1G / 100M | 2× SFP28,25G / 10G | 2× SFP56,50G / 25G / 10G | 1× QSFP28,100G / 40G / 4×25G / 4×10G |
| PCIe | Gen3 x8(硬核封顶 8 GT/s) | Gen3 x8 | Gen3 x8 | Gen5 x8 | Gen3 x16 或 Gen4 x8(PCIE4C 硬核),可拆双 x8 |
| 硬件时间戳 | 4ns(datasheet,User Guide 写 6.2ns) | 6.2ns(161.13 MHz 计数器) | 硬件粒度 <8ns,API 1/4ns 分辨率 | API 亚 ns 分辨率(Onload 9.0+),硬件粒度未公开 | 由固件决定 |
| 发送路径 | 卡上 TX buffer 128KiB/port,可选 1MB 固件(+20ns),支持 preload | 卡上 TX buffer 128KiB/port,支持 preload | PIO(16× 4KB buffer)+ CTPIO | CTPIO(PCIe cut-through),无 PIO | 由固件决定 |
| 硬件过滤 | 128 IP + 64 MAC 规则/port,零延迟代价 | 128 IP + 64 MAC 规则/port,零延迟代价 | 硬件 filter + 组播硬件复制 | Enterprise 通道硬件复制,Express 通道无(需 SHRUB) | 固件内解码过滤 |
| 卡上内存 | 可选 4GB DDR4(仅 FDK 用) | 无 | 无 | 无 | 8GB HBM2,无 DDR |
| 软件栈 | libexanic / exasock,驱动 exanic | libexanic / exasock,驱动 exanic | ef_vi / TCPDirect / Onload,驱动 sfc | ef_vi / TCPDirect / Onload,驱动 sfc | 驱动enyx_hfp |
| 可编程 | FDK,用户逻辑进 FPGA fabric | FDK,用户逻辑进 FPGA fabric | 不可 | 不可 | Vitis / 厂商 bitstream |
| 形态 / 功耗 | Low-profile,117×68mm,0–55°C | Low-profile,117×68mm,0–55°C | Low-profile | Low-profile,<25W,被动散热 | HHHL,75W,被动散热 |
注: ExaNIC 口径是单机进一次出一次:X25 datasheet 叫 trigger-to-response,X10 datasheet 叫 application to network to application;Solarflare 口径是 ping-pong 的 ½RTT。三者都穿两次 PCIe,数量级可比,测试主机和年代不同。
Last Words
总的来说,X25 确实是张很完美的卡,但是 Solarflare X5522 如果来了呢?不再更新的产品线总归是要被淘汰的。