第1章:延迟的度量——微秒级 vs 毫秒级
做低延迟系统,第一个要搞明白的问题就是:你的延迟到底是多少?
我见过不少团队,一上来就追求「极致压缩」,结果CPU被打满,延迟反而飙升。说白了,不知道自己在哪个量级上玩,是最大的坑。
1.1 微秒与毫秒:差三个数量级
先看一组直观数据:
| 单位 | 数值 | 典型场景 |
|---|---|---|
| 1 毫秒 (ms) | 1,000 微秒 | 磁盘I/O、网络RTT |
| 1 微秒 (μs) | 1,000 纳秒 | L3缓存访问、内存拷贝 |
| 100 纳秒 (ns) | 0.1 微秒 | L1缓存命中、简单指令 |
你想想看,如果你的压缩算法耗时 500 微秒,在毫秒级场景下根本不算事。但在微秒级场景里,这 500 微秒就是半条命。
核心观点:延迟度量决定了你的压缩策略选择。毫秒级可以上重型算法(如zstd级别19),微秒级只能上轻量级(如LZ4、Snappy)。
1.2 我踩过的坑:把微秒当毫秒算
几年前做高频交易中间件,我们用了zstd level 3压缩行情数据。测试环境一切正常,延迟稳定在 200 微秒左右。
结果一上生产,延迟直接飙到 2 毫秒。查了半天,发现是CPU缓存被压缩过程冲垮了——压缩本身不慢,但把其他线程的缓存都挤没了。
嗯,这里要注意:延迟度量不能只看平均,要看尾延迟(P99/P999)。我后来养成了习惯,每次压测必看P99,否则就是给自己埋雷。
1.3 压缩吞吐量与CPU开销的平衡
说白了,这就是个用CPU时间换带宽的买卖。但CPU时间不是无限的,你得算清楚账。
我一般用这个公式来评估:
有效吞吐量 = 压缩后数据量 / (压缩时间 + 传输时间 + 解压时间)
别只看压缩率。压缩率再高,如果压缩慢到拖垮CPU,整体吞吐反而下降。
常见压缩算法对比(我实测的数据)
| 算法 | 压缩速度 (MB/s) | 解压速度 (MB/s) | 压缩率 | CPU开销 | 适用延迟场景 |
|---|---|---|---|---|---|
| LZ4 | ~500 | ~2000 | 2.0x | 极低 | 微秒级 |
| Snappy | ~400 | ~1500 | 2.2x | 低 | 微秒级 |
| zstd level 1 | ~300 | ~1000 | 2.8x | 中 | 毫秒级 |
| zstd level 19 | ~10 | ~800 | 4.5x | 极高 | 秒级 |
我的经验:微秒级场景首选LZ4,它解压速度极快,CPU开销几乎可以忽略。毫秒级场景用zstd level 1-3,性价比最高。
1.4 如何测量你的延迟?
别信框架自带的日志。我建议用RDPMC指令或者clock_gettime(CLOCK_MONOTONIC_RAW)来打点。
一个简单的测量模板:
// 伪代码示例
uint64_t start = read_tsc(); // 读CPU时间戳
compressed_data = compress(raw_data);
uint64_t end = read_tsc();
double us = (end - start) / cpu_freq_mhz;
printf("压缩耗时: %.2f 微秒\n", us);
为什么要用TSC?因为它开销极小,大约 20-30 纳秒。用gettimeofday?那玩意儿本身就要几百纳秒,测微秒级延迟根本不准。
避坑指南:我曾经用std::chrono测延迟,结果发现测量本身的开销比压缩还大。后来改用TSC,才看清真实数据。记住:测量工具不能比被测对象还慢。
1.5 知识体系总览
下面这张图,是我对本章核心逻辑的总结。你可以把它当作后续学习的导航图:
1.6 小结
这一章其实就讲了三件事:
- 认清你的延迟量级——微秒还是毫秒,决定了选什么算法
- 平衡CPU开销与吞吐——压缩率不是唯一指标,有效吞吐才是王道
- 用对测量工具——TSC是微秒级测量的好朋友
我个人习惯,每次做压缩方案前,先画一张类似的决策树。把延迟预算、CPU余量、数据特征写上去,再选算法。这样不容易跑偏。
下一章,我们会深入LZ4的实现细节,看看它为什么能在微秒级完成压缩。但在此之前,建议你先拿TSC测测你当前系统的延迟基线——没有基线,就没有优化。