连续rdtsc调用的时间间隔为何呈现周期性?
连续rdtsc调用间隔的周期性波动分析
测试环境与方法
- 实例类型:AWS c5a.8xlarge
- 操作系统:Ubuntu 22.04 LTS服务器(无额外用户软件包,无其他工作负载)
- 测试依据:参考David Gross的演讲思路,对连续rdtsc调用的时间间隔做性能分析
- 测试方法:每秒执行一次256次连续rdtsc调用,计算255个间隔的均值和标准差,每个数据点对应一组这样的均值/标准差
- 时间转换:从
cpuinfo获取CPU频率为2799.932 MHz,将rdtsc计数值除以2.799932转换为纳秒
测试结果
测试结果呈现明显的周期性波动,如下图所示:

均值/标准差计算代码
单个数据点的均值/标准差计算代码如下:
std::pair<double, double> trial(std::size_t nSamples) { folly::fbvector<uint64_t> samples(nSamples); for (uint64_t& s : samples) { s = folly::hardware_timestamp(); } folly::fbvector<uint64_t> diff1(nSamples-1); for (std::size_t i = 0; i < nSamples-1; i++) { diff1[i] = samples[i+1] - samples[i]; } std::cout << folly::format("{}, {}, {}", diff1[0], diff1[1], diff1[2]) << std::endl; double sum = std::accumulate(diff1.begin(), diff1.end(), 0.0); double mean = sum / diff1.size(); folly::fbvector<double> diff1Diff(diff1.size()); std::transform(diff1.begin(), diff1.end(), diff1Diff.begin(), [mean](double x) { return x - mean; }); double sq_sum = std::inner_product(diff1Diff.begin(), diff1Diff.end(), diff1Diff.begin(), 0.0); double stddev = std::sqrt(sq_sum / diff1Diff.size()); std::cout << folly::format("mean:{}, stddev:{}", mean, stddev) << std::endl; return std::make_pair(mean, stddev); }
周期性波动的可能原因
1. CPU动态调频(DVFS)
AWS c5a实例采用AMD EPYC处理器,支持动态电压频率调节。即使系统空载,CPU也会在不同频率档位间周期性切换。rdtsc的计数速率绑定CPU基准频率,但指令实际执行周期随当前运行频率变化,导致调用间隔出现周期性波动。
2. 超线程与内核调度干扰
c5a.8xlarge有16个物理核心、32个逻辑核心。即使测试线程固定在某核心,同一物理核心的另一个逻辑线程可能被调度执行轻量系统任务(如时钟中断处理),短暂抢占测试线程的执行时间,引发rdtsc间隔的周期性延迟。
3. 系统周期性任务
即使无用户负载,内核仍会运行周期性任务:
- 每1ms触发一次的定时器中断
kworker、jbd2等内核守护进程的周期性运行
这些任务会干扰rdtsc调用的连续执行,导致间隔均值和标准差出现周期性波动。
4. 云虚拟机调度
作为云虚拟机,AWS宿主机的周期性调度(如CPU时间片轮转、宿主机资源调度)会传递到虚拟机内部,导致rdtsc调用的执行延迟出现周期性变化。
内容的提问来源于stack exchange,提问作者Long Bu
相关产品推荐
相关产品推荐

