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.5个RTT(往返时间)。
- 拥塞控制:慢启动、拥塞避免,都会增加延迟。
- 重传机制:丢包后要等超时重传,这个时间可能长达几百毫秒。
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 一张图看懂延迟拆解
说了这么多,我们来画一张图,把整个延迟链路串起来。
这张图把延迟拆成了五层。每一层都有它的瓶颈。你优化了应用层,但物理层没动,总延迟还是降不下来。反过来也一样。
1.7 回测系统为什么慢?
现在回到最初的问题:回测系统为什么慢?
说白了,就是每一层都在拖后腿。物理层有光速限制,链路层有网卡中断,网络层有路由跳数,传输层有TCP握手,应用层有序列化开销。这些延迟叠加起来,回测系统自然跑不快。
我见过一个典型的回测系统,数据从交易所到策略引擎,总延迟大约50毫秒。拆解下来:
- 物理层:12ms(深圳到上海往返)
- 链路层:2ms(网卡处理)
- 网络层:15ms(路由跳转)
- 传输层:10ms(TCP握手+拥塞控制)
- 应用层:11ms(HTTP+JSON解析)
你看,每一层都有优化空间。但很多人只盯着应用层优化,忽略了其他层。结果呢?代码写得再快,总延迟也就降了10%。
核心思路:优化回测系统,要从全局出发。每一层的延迟都要压到极致,才能实现真正的低延迟。
嗯,这一章的内容就到这里。下一章,我们会深入网络栈,看看如何从内核态到用户态,一步步优化数据通路。