2. 性能度量标准:延迟、吞吐量、抖动、CPU周期计数、如何正确测量性能
做高频交易,说白了就是跟时间赛跑。你写的代码到底快不快,不能靠感觉,得靠数据说话。这一章,我们就来聊聊那几个最核心的性能指标,以及怎么把它们量出来。
2.1 延迟:从事件到响应的距离
延迟,就是一件事从发生到被处理完,中间花了多少时间。在期货高频里,这个“事”通常是行情数据包到达,或者交易信号产生。
关键点:
- 端到端延迟:从网卡收到数据包,到交易指令发出,整个链条的总耗时。
- 单向延迟 vs 往返延迟:我们更关心单向延迟。因为行情来了,你只需要发指令出去,不需要等交易所确认再发。
- 平均延迟 vs 尾延迟:平均延迟好看没用,99.9%甚至99.99%的尾延迟才是杀手。我见过一个系统,平均延迟只有5微秒,但偶尔一次卡到100微秒,直接导致订单没抢到。
核心原则:在高频交易中,我们优化的是尾延迟,而不是平均延迟。
2.2 吞吐量:单位时间能处理多少事
吞吐量,就是系统在单位时间内能处理多少个事件。比如,一秒钟能处理多少笔行情,或者一秒钟能发出多少笔订单。
延迟和吞吐量,有时候是矛盾的。你想想看,为了降低单笔延迟,你可能用了更简单的算法,但这样单位时间处理的事件数就少了。反过来,为了提升吞吐量,你可能用了批处理,但单笔延迟就上去了。
我个人习惯:先保证延迟达标,再考虑吞吐量。因为在高频场景下,错过一笔交易的机会成本,远大于处理慢一点的成本。
2.3 抖动:延迟的稳定性
抖动,就是延迟的波动程度。一个系统,如果延迟忽高忽低,那比延迟高一点更可怕。为什么?因为你的策略没法预测下一次响应什么时候来。
衡量方式:
- 标准差:延迟数据的标准差越小,抖动越小。
- 百分位差:比如P99延迟减去P50延迟,这个差值越小,说明系统越稳定。
避坑指南:我曾经遇到过一个系统,平均延迟很低,但抖动特别大。查了半天,发现是垃圾回收(GC)在作祟。所以,用C++做高频,一定要避免动态内存分配,尤其是不要在热路径上new/delete。
2.4 CPU周期计数:最底层的度量
CPU周期,是CPU执行一条指令的最小时间单位。比如,一个3GHz的CPU,一个周期就是0.33纳秒。用CPU周期来度量性能,比用纳秒更精确,因为它不受CPU频率变化的影响。
怎么获取?
在x86平台上,可以用RDTSC指令。但要注意,现代CPU的TSC(时间戳计数器)可能不是恒定的,尤其是在不同核心之间。所以,我建议用__rdtscp,它会序列化指令,保证准确性。
// 获取CPU周期计数
#include <x86intrin.h>
uint64_t start = __rdtscp(&aux);
// ... 你的代码 ...
uint64_t end = __rdtscp(&aux);
uint64_t cycles = end - start;
小技巧:测量极短代码段时,要减去测量本身的开销。可以先测一个空循环的周期数,然后从结果中减去。
2.5 如何正确测量性能
测量性能,比写代码更容易犯错。我见过太多人,测出来的数据根本不准,还拿这个去优化。
测量原则:
- 预热:CPU有缓存,有分支预测器。第一次运行和第二次运行,性能可能差很多。所以,先跑几万次“热身”,再开始正式测量。
- 隔离:测量时,关掉其他进程,尤其是那些会抢占CPU的。最好绑核,把测量线程固定在一个物理核心上。
- 多次测量:单次测量没有意义。至少要测100万次,然后看分布。我习惯用直方图来观察延迟分布。
- 避免编译器优化:用
volatile或者asm volatile("" ::: "memory")来防止编译器把测量代码优化掉。
// 一个简单的测量框架
void benchmark() {
// 预热
for (int i = 0; i < 100000; ++i) {
do_work();
}
// 正式测量
std::vector<uint64_t> latencies;
latencies.reserve(1000000);
for (int i = 0; i < 1000000; ++i) {
uint64_t start = __rdtscp(&aux);
do_work();
uint64_t end = __rdtscp(&aux);
latencies.push_back(end - start);
}
// 分析结果
// 计算平均、P50、P99、P99.9等
}
2.6 知识体系图
下面这张图,把性能度量的几个维度串起来了。你可以看到,延迟、吞吐量、抖动、CPU周期,它们之间是相互关联的。
2.7 总结
性能度量,是优化的前提。没有准确的度量,优化就是瞎忙活。记住,延迟、吞吐量、抖动、CPU周期,这四个指标要一起看,不能只看一个。
嗯,这一章就到这里。下一章,我们会聊聊如何用工具把这些指标抓出来,以及怎么分析它们。