std::chrono::steady_clock 是唯一靠谱的选择,因其单调递增且不受系统时间调整(如ntp校时)影响,避免网络延迟计算出现负值或抖动;实操中应全程使用其 now() 和 duration,避免隐式转换、谨慎处理截断与四舍五入,并注意跨进程/跨机器时间不可直接对齐。

为什么 std::chrono::steady_clock 是唯一靠谱的选择
因为网络延迟计算必须避开系统时间跳变(如 NTP 校时),而 std::chrono::system_clock 会受其影响,导致算出负延迟或突增抖动。只有 std::chrono::steady_clock 保证单调递增且不受系统时间调整干扰。
实操建议:
- 始终用
steady_clock::now()记录发送和接收时间点,不要混用其他 clock - 避免转换为
time_t或std::time_point<system_clock></system_clock>,这会引入隐式转换风险 - 在 Windows 上,
steady_clock底层通常映射到QueryPerformanceCounter,精度可达微秒级;Linux 下依赖CLOCK_MONOTONIC,同样满足毫秒甚至亚毫秒需求
duration_cast<milliseconds></milliseconds> 的截断陷阱
直接用 duration_cast<milliseconds></milliseconds> 会向零截断(truncation),比如 1.9ms 变成 1ms,误差累积后对 RTT 统计影响明显。这不是四舍五入,是砍掉小数部分。
实操建议:
- 若需保留原始精度做后续统计(如 P95、标准差),全程用
nanoseconds或microseconds存储差值,最后再按需转整数毫秒 - 真要四舍五入到毫秒:先转
nanoseconds,加 500000ns 再 cast:duration_cast<milliseconds>(diff + 500000ns)</milliseconds> - 注意
auto diff = end - start的类型是steady_clock::duration,它底层可能是纳秒或微秒,别假设它是毫秒
跨线程/跨进程时间戳对齐的常见崩坏点
单机内两个线程用 steady_clock::now() 拿到的时间戳,理论上可直接相减。但一旦涉及多进程(如 client/server 分开进程)、容器环境或虚拟机,不同进程看到的 steady_clock 起始点可能不一致,尤其在容器冷启动或 VM 迁移后。
实操建议:
- 纯本地延迟测试(如 loopback)没问题;真实网络链路必须走端到端时间同步,哪怕只是简单 NTP 对齐(
chrony或ntpd) - 如果 server 和 client 都在同一个物理机上,且不重启,
steady_clock差值仍可靠;但只要涉及远程机器,就必须放弃“本地 clock 相减”幻想 - 更稳妥的做法:client 发送带本地时间戳的包,server 回包时附上接收/发送时间戳,client 用
(recv_time - send_time) - (server_recv - server_send)算单向延迟(需假设往返对称)
高频率采样下的性能与精度权衡
每毫秒调一次 steady_clock::now() 在多数现代 CPU 上开销极小(几十纳秒),但频繁调用仍会拖慢热点路径。更隐蔽的问题是:某些嵌入式或旧版 glibc 环境下,steady_clock 实现可能退化为 gettimeofday,精度跌至毫秒级甚至更差。
实操建议:
- 用
steady_clock::period::num / steady_clock::period::den检查实际精度(例如1/1000000000表示纳秒级) - 在 Linux 上运行
cat /proc/sys/kernel/timer_resolution查看内核 timer tick 分辨率,低于 1ms 才适合毫秒级延迟测量 - 压测场景下,避免在 tight loop 中反复调用
now();可先批量收包,再统一打时间戳,或用 per-CPU 时间戳缓存减少 syscall
真正难的不是拿到毫秒值,而是确认这个毫秒值在你的部署环境中是否稳定、可复现、跨节点可比。clock 源、内核配置、容器 runtime、甚至 BIOS 中的 TSC 同步开关,都可能让同一段代码在不同机器上给出完全不同的结果。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











