第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 知识体系总览

下面这张图,是我对本章核心逻辑的总结。你可以把它当作后续学习的导航图:

延迟度量与压缩平衡决策树 你的延迟目标? 微秒级 (≤100μs) 毫秒级 (1-10ms) 算法:LZ4 / Snappy 测量:TSC / RDPMC 算法:zstd level 1-3 关注:P99尾延迟 核心原则:用最少的CPU开销,满足你的延迟预算 不要为了10%的压缩率,牺牲50%的吞吐

1.6 小结

这一章其实就讲了三件事:

  • 认清你的延迟量级——微秒还是毫秒,决定了选什么算法
  • 平衡CPU开销与吞吐——压缩率不是唯一指标,有效吞吐才是王道
  • 用对测量工具——TSC是微秒级测量的好朋友

我个人习惯,每次做压缩方案前,先画一张类似的决策树。把延迟预算、CPU余量、数据特征写上去,再选算法。这样不容易跑偏。

下一章,我们会深入LZ4的实现细节,看看它为什么能在微秒级完成压缩。但在此之前,建议你先拿TSC测测你当前系统的延迟基线——没有基线,就没有优化


专注资料整理