一、硬件选型:为低延迟打下地基
做市商交易系统,说白了就是跟时间赛跑。我见过太多团队,代码写得花里胡哨,结果硬件选型一塌糊涂,延迟死活降不下来。今天咱们就从硬件底层开始,把地基打牢。
1.1 CPU:高频与核心隔离
CPU是交易系统的心脏。我个人习惯,首选Intel Xeon Scalable系列,比如Gold 6248或Platinum 8380。为什么?因为它们的单核频率高,而且支持超线程。
但这里有个坑——超线程。你想想看,两个逻辑核共享一个物理核的缓存和执行单元,一旦一个核跑交易逻辑,另一个核跑系统任务,缓存被频繁刷新,延迟直接飙升。我曾在项目中遇到过,开启超线程后,订单处理延迟从5微秒跳到了12微秒。
核心隔离怎么做?用Linux的isolcpus内核参数。比如:
# 在GRUB_CMDLINE_LINUX中添加
isolcpus=2-15,18-31
这样就把物理核2-15和18-31隔离出来了,系统进程不会跑到这些核上。然后你用taskset把交易进程绑定到这些核上:
taskset -c 2,3 ./trading_engine
嗯,这里要注意:隔离的核数要留够。我一般留2个核给系统,剩下的全给交易进程。
1.2 内存:NUMA绑定
现代服务器都是NUMA架构。什么意思?就是每个CPU有自己的内存控制器,访问本地内存快,访问远端内存慢。差距有多大?本地内存延迟约70ns,远端内存延迟约130ns——几乎翻倍。
所以,你必须把交易进程绑定到同一个NUMA节点上。用numactl命令:
numactl --cpunodebind=0 --membind=0 ./trading_engine
这样进程的CPU和内存都在节点0上,避免了跨节点访问。我见过一个团队,代码优化得再好,结果内存跨NUMA节点,延迟硬生生多了50ns。
numastat监控内存分配情况。如果看到大量跨节点分配,说明你的绑定没做好。
1.3 网卡:Solarflare vs Intel X710
网卡是交易系统的咽喉。我个人强烈推荐Solarflare SFN8522或X2522系列。为什么?因为它们支持OpenOnload——一种用户态网络协议栈,能绕过内核直接收发网络包。
Intel X710也不错,但更适合通用场景。如果你做高频做市,Solarflare是首选。我曾在项目中对比过:用Solarflare + OpenOnload,UDP延迟从10微秒降到了2微秒。
| 特性 | Solarflare SFN8522 | Intel X710-DA2 |
|---|---|---|
| 用户态网络 | 支持(OpenOnload) | 不支持原生 |
| 硬件时间戳 | 支持(纳秒级) | 支持(微秒级) |
| 典型延迟 | 1-2微秒 | 5-10微秒 |
| 适用场景 | 高频做市 | 通用交易 |
配置Solarflare网卡时,记得开启硬件时间戳和RSS(接收端缩放):
# 开启硬件时间戳
ethtool -T eth0
# 设置RSS队列数
ethtool -L eth0 combined 4
1.4 NVMe SSD:存储的最后一公里
做市商系统需要记录大量订单和行情数据。传统SATA SSD的延迟在100微秒左右,而NVMe SSD能做到10微秒以下。我推荐Samsung PM983或Intel P4610系列。
但光有NVMe还不够。你得用blk-mq多队列驱动,让每个CPU核都能直接访问存储队列。配置方法:
# 查看NVMe设备
nvme list
# 设置多队列
echo 4 > /sys/block/nvme0n1/queue/nr_requests
二、操作系统调优:让硬件跑出极限
硬件选好了,操作系统也得配合。Linux内核默认是为通用场景优化的,你得手动调一调。
2.1 irqbalance:中断要绑对核
irqbalance是个好东西,它会自动把中断分配到不同CPU核上。但做市商系统不需要它——我们要手动绑定中断。
先关掉irqbalance:
systemctl stop irqbalance
systemctl disable irqbalance
然后手动绑定网卡中断到指定核:
# 查看网卡中断
cat /proc/interrupts | grep eth0
# 绑定中断到CPU 2
echo 4 > /proc/irq/123/smp_affinity
这里smp_affinity是位掩码。CPU 2对应二进制100,即十进制4。我习惯把网卡中断绑到交易进程旁边的核上,这样缓存命中率更高。
2.2 CPU Governor:别让频率乱跳
Linux默认的CPU调频策略是ondemand或powersave,频率会动态变化。这对做市商系统是灾难——频率切换本身就有延迟,而且不稳定。
我建议用performance模式,让CPU始终跑在最高频率:
# 设置所有核为performance模式
for cpu in /sys/devices/system/cpu/cpu[0-9]*; do
echo performance > $cpu/cpufreq/scaling_governor
done
你还可以用cpupower工具:
cpupower frequency-set -g performance
scaling_governor是只读的。
2.3 网络协议栈优化:从内核到用户态
网络协议栈是延迟的大头。Linux内核默认的TCP/IP栈,从网卡到应用,要经过中断处理、软中断、协议解析、socket层……每一步都有开销。
我推荐的做法是:
- 使用用户态网络协议栈:比如Solarflare的OpenOnload,或者DPDK。直接绕过内核,在用户态收发网络包。
- 调整内核参数:如果不用用户态栈,至少把内核参数调优。
内核参数调优示例:
# 网络缓冲区调大
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
# 关闭TCP时间戳
net.ipv4.tcp_timestamps = 0
# 关闭TCP选择确认
net.ipv4.tcp_sack = 0
# 关闭TCP窗口缩放
net.ipv4.tcp_window_scaling = 0
# 减少TIME_WAIT
net.ipv4.tcp_tw_reuse = 1
但说实话,这些参数优化效果有限。真正要低延迟,还是得上用户态协议栈。我曾在项目中用DPDK,把UDP收发延迟从8微秒降到了1.5微秒。
三、知识体系总览
下面这张图,是我对本章知识体系的总结。你可以把它当作一个检查清单:
这张图把硬件选型和OS调优串起来了。你照着这个清单一步步做,延迟至少能降一半。
好了,硬件和OS调优就讲到这里。下一章咱们聊聊数据结构和内存管理——那才是真正考验C++功底的地方。
公众号:蓝海资料掘金营,微信deep3321