1. 网络延迟的根源:从物理层到应用层的延迟拆解

做量化回测的朋友,十有八九都遇到过这种情况:策略逻辑明明没问题,回测跑起来却慢得像蜗牛。你盯着进度条,心里直犯嘀咕——到底是哪一步拖了后腿?

我刚开始做回测系统时,也踩过这个坑。那时候我天真地以为,只要把代码写得够快,回测就能飞起来。结果呢?数据一多,网络先崩了。后来我才明白,回测系统的瓶颈,往往不在计算,而在网络

这一章,我们就来拆解网络延迟的根源。从物理层一路看到应用层,搞清楚回测系统到底为什么慢。

1.1 物理层:光速是硬伤

先说个最底层的限制——光速。你可能觉得这跟回测没关系,但实际影响比你想象的大。

数据从你的机器传到交易所服务器,走的是光纤。光纤里的光速大约是每秒20万公里,比真空中的30万公里慢一些。假设你的服务器在深圳,交易所在上海,距离大约1200公里。那么,单程传输时间就是:

1200 km / 200,000 km/s = 6 毫秒

来回就是12毫秒。这还只是物理层的最小延迟,还没算上中间的路由器、交换机、网卡处理时间。

关键点:物理距离决定了延迟的下限。你代码写得再好,也突破不了光速的限制。

我在项目中遇到过一件事:有个团队把回测服务器从北京搬到了上海,结果延迟直接降了30%。原因很简单——离交易所更近了。所以,如果你做高频回测,服务器选址是第一位的。

1.2 数据链路层:网卡和驱动

过了物理层,数据到了网卡。这里有两个关键因素:网卡硬件驱动

普通网卡处理一个数据包,大概需要几十微秒。但如果你用上了专用网卡(比如Solarflare、Mellanox),这个时间可以压到几微秒。为什么差这么多?

  • 中断处理:普通网卡每收到一个包,就触发一次CPU中断。中断多了,CPU忙着切换上下文,延迟自然高。
  • 零拷贝:专用网卡支持DMA(直接内存访问),数据直接从网卡到应用内存,省掉了内核态到用户态的拷贝。

我的建议:如果你的回测系统对延迟敏感,别省网卡的钱。一块好的网卡,比优化代码效果来得快。

1.3 网络层:路由和拥塞

数据从网卡出来,进入网络层。这里的问题主要是路由跳数拥塞控制

你想想看,数据包从你的机器到交易所,中间要经过多少个路由器?每经过一个路由器,就要做一次路由查找和转发。这个时间虽然短(微秒级),但积少成多。

我见过一个案例:某团队的回测系统,数据包要经过15个路由器跳转,总延迟比直连多了20毫秒。后来他们换了专线,跳数降到3个,延迟直接砍半。

拥塞控制也是个坑。TCP协议有拥塞避免机制,一旦检测到丢包,就会主动降低发送速率。这在普通网络上是好事,但在回测场景下,丢包就意味着延迟飙升

注意:回测系统里,网络拥塞导致的延迟抖动,比固定延迟更可怕。因为它不可预测,你的策略可能因此出现异常信号。

1.4 传输层:TCP vs UDP

到了传输层,你面临一个经典选择:TCP还是UDP?

特性 TCP UDP
可靠性 保证不丢包 不保证
延迟 较高(握手+重传) 低(无握手)
适用场景 历史数据回放 实时行情

TCP的延迟主要来自三方面:

  1. 三次握手:建立连接需要1.5个RTT(往返时间)。
  2. 拥塞控制:慢启动、拥塞避免,都会增加延迟。
  3. 重传机制:丢包后要等超时重传,这个时间可能长达几百毫秒。

UDP就简单多了——发出去就不管了。延迟低,但可能丢包。在回测系统里,我一般这样用:

  • 历史数据回放:用TCP,保证数据完整。
  • 实时行情:用UDP,丢几个包影响不大,但延迟必须低。

避坑指南:我曾经在回测系统里全用TCP,结果实时行情延迟高得离谱。后来改成UDP,延迟降了80%。但要注意,UDP丢包需要应用层自己处理。

1.5 应用层:协议和序列化

最后到了应用层。这里的问题,往往是协议设计数据序列化

很多回测系统用HTTP协议传输数据。HTTP本身没问题,但它的请求-响应模型,天然不适合低延迟场景。每次请求都要建立连接、发送头部、等待响应。你想想看,一个HTTP请求的头部可能就有几百字节,而真正有用的数据只有几十字节。

序列化也是个坑。JSON、XML这些文本协议,解析起来慢。我见过一个系统,用JSON传输行情数据,序列化+反序列化花了5毫秒。换成Protobuf后,这个时间降到了0.5毫秒。

// 低延迟序列化示例(Protobuf)
message MarketData {
  int64 timestamp = 1;
  string symbol = 2;
  double price = 3;
  int64 volume = 4;
}

// 序列化耗时:约0.3微秒
// JSON序列化耗时:约5微秒

我的经验:在应用层,尽量用二进制协议,少用文本协议。数据格式越紧凑,解析越快。

1.6 一张图看懂延迟拆解

说了这么多,我们来画一张图,把整个延迟链路串起来。

网络延迟拆解:从物理层到应用层 物理层 光速限制:6ms/1000km | 光纤传输 | 信号衰减 数据链路层 网卡中断:10-50μs | DMA零拷贝 | 驱动优化 网络层 路由跳数:1-5ms/跳 | 拥塞控制 | 丢包重传 传输层 TCP三次握手:1.5RTT | 拥塞窗口 | UDP无连接 应用层 HTTP头部:500字节 | JSON解析:5ms | Protobuf:0.5ms 总延迟 = 物理层 + 链路层 + 网络层 + 传输层 + 应用层

这张图把延迟拆成了五层。每一层都有它的瓶颈。你优化了应用层,但物理层没动,总延迟还是降不下来。反过来也一样。

1.7 回测系统为什么慢?

现在回到最初的问题:回测系统为什么慢?

说白了,就是每一层都在拖后腿。物理层有光速限制,链路层有网卡中断,网络层有路由跳数,传输层有TCP握手,应用层有序列化开销。这些延迟叠加起来,回测系统自然跑不快。

我见过一个典型的回测系统,数据从交易所到策略引擎,总延迟大约50毫秒。拆解下来:

  • 物理层:12ms(深圳到上海往返)
  • 链路层:2ms(网卡处理)
  • 网络层:15ms(路由跳转)
  • 传输层:10ms(TCP握手+拥塞控制)
  • 应用层:11ms(HTTP+JSON解析)

你看,每一层都有优化空间。但很多人只盯着应用层优化,忽略了其他层。结果呢?代码写得再快,总延迟也就降了10%。

核心思路:优化回测系统,要从全局出发。每一层的延迟都要压到极致,才能实现真正的低延迟。

嗯,这一章的内容就到这里。下一章,我们会深入网络栈,看看如何从内核态到用户态,一步步优化数据通路。

专注资料整理