1. 性能基准与目标设定:定义延迟、吞吐量、抖动等核心指标,设定SLA目标
做量化交易系统,第一件事不是写代码,而是搞清楚一个问题:你到底要优化什么?
我见过太多团队,上来就怼着某个指标猛干,结果上线后发现瓶颈根本不在这。说白了,没有基准,就没有方向。没有目标,优化就是瞎忙活。
这一章,我们先把「尺子」造出来。你得知道量什么,怎么量,量到多少才算合格。
1.1 核心指标:延迟、吞吐量、抖动
交易系统里,我们最关心的就三个指标。嗯,其实所有高性能系统都差不多,但交易场景对它们的要求格外苛刻。
1.1.1 延迟(Latency)
延迟就是「从事件发生到系统响应」的时间差。在交易里,这个事件可能是行情数据到达,也可能是订单提交。
我个人习惯把延迟拆成三部分来看:
- 网络延迟:数据在网线上跑的时间。这个基本由物理距离决定,光速是上限。
- 处理延迟:CPU 在计算、解码、校验上花的时间。这是咱们能优化的主战场。
- 排队延迟:数据在队列里等着被处理的时间。很多人忽略这个,其实它往往是最大的坑。
关键认知: 延迟不是平均值,而是百分位值。P50(中位数)好看没用,P99.9 才是用户真实体验。做交易,我们甚至看 P99.999。
我在项目中遇到过一件事:某个策略回测时平均延迟只有 5 微秒,团队高兴得不行。结果一上实盘,频繁出现 200 微秒的尖峰。查到最后,是 GC 暂停导致的。你看,只看平均值,你永远不知道系统什么时候会「卡一下」。
1.1.2 吞吐量(Throughput)
吞吐量是单位时间内系统能处理多少笔交易或多少条行情。说白了,就是「系统有多能扛」。
延迟和吞吐量其实是跷跷板关系。你想想看,一味追求低延迟,可能会限制并发处理能力;反过来,拼命压榨吞吐量,延迟必然上升。
我建议用 Little's Law 来理解这个关系:
L = λ × W
其中 L 是系统中平均请求数,λ 是吞吐量,W 是平均延迟。这个公式告诉我们:在系统容量固定的情况下,吞吐量和延迟是绑定的。
| 场景 | 典型吞吐量要求 | 说明 |
|---|---|---|
| 高频做市 | 10万+ 订单/秒 | 极低延迟优先,吞吐量靠硬件堆 |
| 中频套利 | 1万-5万 订单/秒 | 延迟和吞吐量需要平衡 |
| 低频策略 | 100-1000 订单/秒 | 吞吐量通常不是瓶颈 |
1.1.3 抖动(Jitter)
抖动是延迟的「波动程度」。为什么单独拿出来说?因为对于交易系统,稳定的低延迟比偶尔极低的延迟更重要。
举个例子:
- 系统 A:延迟稳定在 10 微秒,几乎没有波动。
- 系统 B:平均延迟 8 微秒,但有时会跳到 100 微秒。
你选哪个?我肯定选 A。因为交易策略依赖的是「确定性」。你不知道那 100 微秒的抖动什么时候来,但你知道它来了就会让你丢单。
避坑指南: 我曾经在一个项目里花了三周优化平均延迟,从 12 微秒降到了 6 微秒。结果上线后,抖动从 2 微秒飙到了 15 微秒。策略直接崩了。后来我才明白,消除抖动比降低延迟更重要。从那以后,我的优化目标里永远把「抖动」放在第一位。
1.2 设定 SLA 目标
指标定义清楚了,接下来就是定目标。SLA(Service Level Agreement)不是随便拍脑袋定的,它得跟业务场景挂钩。
1.2.1 如何设定合理的 SLA
我一般分三步走:
- 摸底:先跑一周的基准测试,拿到当前系统的真实数据。没有数据,一切免谈。
- 对标:看看竞品或者行业标准。比如,交易所的行情推送延迟通常在 10-50 微秒,你的系统如果比这个慢,那策略就没优势。
- 折中:在理想和现实之间找平衡。别一上来就定「P99 延迟 1 微秒」,那不叫目标,叫做梦。
一个典型的 SLA 示例:
- 延迟:P50 < 5μs,P99 < 15μs,P99.9 < 50μs
- 吞吐量:稳定支持 50,000 订单/秒,峰值 100,000 订单/秒
- 抖动:P99 延迟与 P50 延迟的差值 < 10μs
1.2.2 别忘了「不可用时间」
很多人定 SLA 只盯着延迟和吞吐量,却忘了另一个关键指标:可用性。
交易系统对可用性的要求极高。哪怕只宕机 1 秒,在极端行情下可能造成数百万的损失。我建议把可用性也纳入 SLA:
| 可用性等级 | 年宕机时间 | 适用场景 |
|---|---|---|
| 99.9% | 8.76 小时 | 低频策略,可接受短暂中断 |
| 99.99% | 52.56 分钟 | 中频策略,需要较高可靠性 |
| 99.999% | 5.26 分钟 | 高频策略,几乎不允许宕机 |
1.3 基准测试:怎么量才准?
指标和 SLA 都定了,接下来就是测。但基准测试本身就有很多坑。
1.3.1 测试环境要「脏」
很多人喜欢在干净的开发环境上跑基准测试,结果数据漂亮得不行。一上生产环境,直接打回原形。
我建议:测试环境要尽量模拟生产环境的「脏」。比如:
- 加上网络延迟模拟
- 混入一些背景流量
- CPU 和内存留一些余量,别跑满
小技巧: 我习惯在基准测试脚本里故意加一些「噪音线程」,模拟其他服务的资源争抢。这样测出来的数据才更接近真实情况。
1.3.2 工具选择
测延迟和吞吐量,工具很重要。我常用的几个:
- wrk / wrk2:测 HTTP 接口的吞吐量和延迟分布,支持自定义线程和连接数。
- JMH(Java Microbenchmark Harness):测 Java 代码片段的微基准,能帮你避开 JIT 预热和 GC 的坑。
- perf / flamegraph:Linux 性能分析工具,配合火焰图看 CPU 到底花在哪了。
举个例子,用 JMH 测一个订单解码函数的延迟:
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void decodeOrder() {
Order order = OrderDecoder.decode(rawBytes);
}
跑完之后,JMH 会告诉你平均延迟、P50、P99 等百分位数据。嗯,这才是靠谱的基准。
1.4 小结:先定标准,再谈优化
这一章的内容其实就一句话:没有基准的优化都是耍流氓。
你想想看,如果你连「延迟是多少」「抖动有多大」都不知道,你怎么知道优化有没有效果?你怎么跟老板交代?
所以,我的建议是:
- 先把延迟、吞吐量、抖动这三个指标定义清楚
- 根据业务场景设定合理的 SLA
- 用靠谱的工具跑基准测试,拿到真实数据
下一章,我们会聊到「如何从代码层面压榨性能」。但在此之前,请确保你已经把这一章的「尺子」造好了。否则,后面的优化你都不知道该往哪个方向使劲。
记住: 性能调优不是玄学,是科学。科学的第一步,就是测量。