使用CLOCK_REALTIME的clock_gettime函数计算耗时出现异常值的问题排查
问题分析与解决方案
嘿,这个问题我之前也碰到过,本质上是选了错误的时钟类型加上无符号整数溢出导致的——咱们一步步拆解清楚:
为什么会出现接近2^64纳秒的异常值?
你推测ts.tv_nsec等于-1其实是个误解,真正的根源是**CLOCK_REALTIME时钟的非单调性**:
CLOCK_REALTIME对应系统的「墙上时间」,也就是我们日常看到的年月日时分秒。这个时钟是可以被系统主动调整的,比如NTP服务在纠正时间偏差时,可能会把时钟往回拨(比如服务器时间快了几分钟,会直接跳回正确时间)。- 当这种时间回拨刚好发生在你记录
start_time和stop_time之间时,stop_time会比start_time小。而你用的是uint64_t无符号整数,stop_time - start_time会触发无符号溢出——按照C标准,无符号整数溢出是模2^N的运算,结果就变成了2^64 - (start_time - stop_time),这就是你看到的那些接近2^64纳秒的巨大数值(转换成秒就是约584年)。
这段代码的核心问题
- 错误选择了时钟类型:
CLOCK_REALTIME不适合用来测量程序运行耗时,因为它会被外部调整,无法保证单调递增。 - 未处理时间倒退的情况:无符号整数减法直接溢出,没有做任何边界检查,导致异常输出。
修复方案
方案1:改用单调时钟(推荐)
用CLOCK_MONOTONIC代替CLOCK_REALTIME,这个时钟是从系统启动开始单调递增的,永远不会被回拨,是统计程序耗时的标准选择:
struct timespec ts; uint64_t start_time; uint64_t stop_time; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { abort(); } start_time = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec; /* some computation... */ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { abort(); } stop_time = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec; printf("%" PRIu64 "\n", (stop_time - start_time + 500000000) / 1000000000);
这个方案从根源上解决了时间回拨的问题,完全适配耗时统计的场景。
方案2:处理时间回拨情况(如果必须用CLOCK_REALTIME)
如果你因为业务需求必须使用CLOCK_REALTIME,那就要检查stop_time是否小于start_time,并做特殊处理:
// ... 前面的时间记录代码不变 ... uint64_t elapsed; if (stop_time >= start_time) { elapsed = (stop_time - start_time + 500000000) / 1000000000; } else { // 时间回拨了,这里可以设为0或者记录日志 elapsed = 0; } printf("%" PRIu64 "\n", elapsed);
内容的提问来源于stack exchange,提问作者DaBler
相关产品推荐
相关产品推荐

