一、高频交易系统概述
什么是高频交易
高频交易,说白了就是「比谁快」的游戏。
我入行那会儿,有个前辈跟我说过一句话,我一直记着:「高频交易不是预测未来,而是比别人更快地响应现在。」
它的核心逻辑很简单:
- 用计算机程序自动完成交易
- 持仓时间极短,通常几秒甚至毫秒级别
- 单笔利润很薄,靠海量交易次数取胜
举个例子你就明白了。假设某只股票在A交易所报价100.01元,在B交易所报价100.00元。你发现了这个价差,立刻在B买入、在A卖出,赚这1分钱。听起来简单吧?但问题是,成千上万的交易者都在盯着这个价差。谁的系统快,谁就能抢到这笔利润。
这就是高频交易的本质——速度就是利润。
核心性能指标
做高频交易系统,有三个指标你必须刻在脑子里。我当年面试团队时,第一个问题就是问这三个指标。
| 指标 | 定义 | 典型要求 |
|---|---|---|
| 延迟 | 从事件发生到系统响应的时间 | 微秒级(1μs = 0.001ms) |
| 吞吐量 | 单位时间内处理的交易数量 | 每秒百万级订单 |
| 抖动 | 延迟的波动幅度 | 纳秒级标准差 |
延迟——最致命的指标
延迟是高频交易的头号敌人。
你想想看,如果别人比你快1微秒,就意味着别人能先看到行情、先下单、先成交。等你反应过来,利润已经被抢走了。
我经历过一个真实案例。有一次,我们的系统延迟突然从5微秒跳到了20微秒。排查了一整天,最后发现是内存分配器在搞鬼——某个线程触发了垃圾回收,导致所有线程都被卡住了。就这15微秒的抖动,让我们当天损失了六位数的利润。
所以,延迟不只是「快不快」的问题,更是「稳不稳」的问题。
延迟的构成:
- 网络延迟:数据在网线上传输的时间
- 内核延迟:操作系统处理网络包的时间
- 用户态延迟:应用程序处理数据的时间
- 内存延迟:数据在内存和CPU之间搬运的时间
吞吐量——别被压垮
吞吐量决定了你能吃下多大的行情。
行情火爆的时候,每秒可能涌入几百万笔订单。如果你的系统处理不过来,订单就会堆积,延迟就会飙升,最终导致丢单。
我记得有一次做压力测试,系统在每秒50万笔订单时表现良好,但到了80万笔就突然崩溃了。查了半天,发现是锁竞争导致的——多个线程同时抢一把锁,CPU时间全耗在等待上了。
嗯,这就是为什么我们要讲锁优化。
抖动——隐藏的杀手
抖动比高延迟更可怕。
为什么?因为高延迟你可以预测,可以提前应对。但抖动是随机的,你根本不知道下一秒系统会不会卡一下。
我见过最夸张的一次抖动,是某家交易所的行情数据突然延迟了100毫秒。就这100毫秒,让所有基于该行情做策略的量化基金集体亏损。原因是什么?是那家交易所的服务器在做内存垃圾回收。
所以,在高频交易领域,确定性比高性能更重要。
为什么需要内存池与锁优化
好,现在我们来聊聊核心问题:为什么需要内存池和锁优化?
说白了,标准的内存分配和锁机制,根本满足不了高频交易的需求。
标准内存分配的问题
你写一个普通的C++程序,用new或malloc分配内存,看起来没什么问题。但在高频交易场景下,问题就大了:
- 分配速度慢:每次分配都要走系统调用,动辄几百纳秒
- 内存碎片:频繁分配释放会导致内存碎片,进一步拖慢速度
- 不可预测:分配时间不确定,可能突然变慢
我曾经在一个项目里,用标准分配器处理行情数据。平时延迟只有2微秒,但每隔几分钟就会突然跳到50微秒。排查后发现,是内存分配器在后台做内存整理。
解决方案是什么?内存池。
内存池提前分配好一大块内存,然后自己管理。分配和释放都在用户态完成,不需要系统调用。速度能快一个数量级,而且时间完全可控。
标准锁的问题
多线程编程离不开锁。但标准库的锁,在高频交易场景下也是问题重重:
- 互斥锁太重:线程切换开销巨大,一次锁竞争可能消耗几微秒
- 自旋锁浪费CPU:忙等待会吃掉CPU时间
- 死锁风险:锁的顺序搞错了,系统直接卡死
我印象最深的一次事故,是团队里一个新人用std::mutex保护一个全局计数器。平时没问题,但行情一爆发,所有线程都在抢这把锁。结果CPU使用率飙到100%,系统延迟从5微秒变成了500微秒。那天我们亏了不少钱。
后来我们改用了无锁数据结构,配合原子操作,才把这个问题解决掉。
我的建议:
不要一上来就想着用无锁编程。先搞清楚你的瓶颈在哪里。很多时候,优化锁的粒度、减少锁的持有时间,比换成无锁结构更有效。
避坑指南:
我曾经见过一个团队,为了追求极致性能,把所有锁都换成了自旋锁。结果在8核机器上跑得好好的,换到32核机器上反而更慢了。为什么?因为自旋锁在高并发下会导致严重的缓存一致性开销。所以,没有银弹,每种方案都有适用场景。
本章小结
高频交易的核心就是「快」和「稳」。延迟、吞吐量、抖动这三个指标,决定了你的系统能不能赚钱。
标准的内存分配和锁机制,在高频交易场景下根本不够用。内存池能解决内存分配的速度和稳定性问题,锁优化能解决多线程竞争的性能问题。
接下来的章节,我会带你一步步实现这些优化。从内存池的设计,到无锁队列的实现,再到实战中的调优技巧。这些都是我踩过无数坑之后总结出来的经验。
准备好了吗?我们开始吧。