RDTSCP与RDTSC+;CPUID
我正在做一些Linux内核计时,特别是在中断处理路径中。我一直在使用RDTSC进行计时,但最近我发现它不一定准确,因为指令可能会发生混乱 然后我试着:RDTSCP与RDTSC+;CPUID,c,assembly,linux-kernel,x86,C,Assembly,Linux Kernel,X86,我正在做一些Linux内核计时,特别是在中断处理路径中。我一直在使用RDTSC进行计时,但最近我发现它不一定准确,因为指令可能会发生混乱 然后我试着: RDTSC+CPUID(此处按相反顺序)刷新管道,在虚拟机(我的工作环境)上由于超级调用等原因产生的开销高达60倍(!)。这包括启用和不启用硬件虚拟化 最近我遇到了RDTSCP*指令,它似乎做了RDTSC+CPUID所做的事情,但效率更高,因为它是一条较新的指令,相对而言,开销只有1.5x-2x 我的问题是:RDTSCP作为一个测量点是否真正准确
- 保存当前循环计数器值
- 执行一种基准测试(即:磁盘、网络)
- 将当前和上一个周期计数器的增量添加到累加器值中,并在每个中断中增加一个计数器
- 最后,将增量/累加器除以中断数,得到每个中断的平均周期成本
*第27页有关您从cpuid指令中看到的开销的完整讨论,请访问。使用rdtsc时,需要使用cpuid以确保执行管道中没有其他指令。rdtscp指令从本质上刷新管道。(参考的SO线程也讨论了这些要点,但我在这里讨论了它们,因为它们也是您问题的一部分) 如果处理器不支持rdtscp,则仅“需要”使用cpuid+rdtsc。否则,rdtscp就是您想要的,并且会准确地为您提供所需的信息 这两条指令都提供了一个64位、单调递增的计数器,表示处理器上的周期数。如果这是您的模式:
uint64_t s, e;
s = rdtscp();
do_interrupt();
e = rdtscp();
atomic_add(e - s, &acc);
atomic_add(1, &counter);
根据您的读取发生的位置,您的平均测量值可能仍有1的偏差。例如:
T1 T2
t0 atomic_add(e - s, &acc);
t1 a = atomic_read(&acc);
t2 c = atomic_read(&counter);
t3 atomic_add(1, &counter);
t4 avg = a / c;
现在还不清楚“[a]t the end”是否指的是一个可以以这种方式竞赛的时代。如果是这样的话,你可能想计算一个运行平均线或移动平均线与你的增量
侧重点:
clock\u gettime
,请参见我的答案的末尾)
为了对我的矩阵乘法代码进行基准测试并将其与理论上的最佳值进行比较,我需要知道经过的时间和经过的周期(或者更确切地说是测试期间的有效频率)
让我介绍三种不同的方法来确定经过的循环数
核心时钟周期李>
第一种方法是最可靠的,但它需要访问BIOS并影响您运行的所有其他方法的性能(当我在我的i5-4250U上禁用动态频率缩放时,它以恒定的1.3 GHz运行,而不是以2.6 GHz为基数)。仅为基准测试而更改BIOS也很不方便
第二种方法在您不想禁用动态频率标度和/或不想为您没有物理访问权限的系统禁用动态频率标度时非常有用。但是,性能监视器计数器需要只有内核或设备驱动程序才能访问的特权指令
第三种方法在没有物理访问权限和特权访问权限的系统上很有用。这是我在实践中使用最多的方法。原则上它是最不可靠的,但实际上它和第二种方法一样可靠
下面是我如何用C确定经过的时间(以秒为单位)
#定义计时器(类型)时钟(实时)
timespec time1、time2;
时钟获取时间(定时器类型和定时器1);
foo();
时钟获取时间(定时器类型和定时器2);
double dtime=时间差(时间1,时间2);
双时差(timespec开始、timespec结束)
{
时间样本温度;
如果((end.tv_nsec-start.tv_nsec)以下代码将确保rdstcp
在正确的时间启动。
RDTSCP
不能执行得太早,但它可以执行得太晚,因为CPU可以将指令移到RDTSCP
之后执行
为了防止这种情况发生,我们根据rdstcp
将其输出放在edx:eax中这一事实创建了一个虚假的依赖链
rdt
for (int i = 0; i < SOME_LARGEISH_NUMBER; i++) {
s = rdtscp();
loop_body();
e = rdtscp();
acc += e - s;
}
printf("%"PRIu64"\n", (acc / SOME_LARGEISH_NUMBER / CLOCK_SPEED));
s = rdtscp();
for (int i = 0; i < SOME_LARGEISH_NUMBER; i++) {
loop_body();
}
e = rdtscp();
printf("%"PRIu64"\n", ((e-s) / SOME_LARGEISH_NUMBER / CLOCK_SPEED));
#define TIMER_TYPE CLOCK_REALTIME
timespec time1, time2;
clock_gettime(TIMER_TYPE, &time1);
foo();
clock_gettime(TIMER_TYPE, &time2);
double dtime = time_diff(time1,time2);
double time_diff(timespec start, timespec end)
{
timespec temp;
if ((end.tv_nsec-start.tv_nsec)<0) {
temp.tv_sec = end.tv_sec-start.tv_sec-1;
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
} else {
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
}
return (double)temp.tv_sec + (double)temp.tv_nsec*1E-9;
}
mfence
lfence
rdtsc
shl rdx, 0x20
or rax, rdx
rdtscp
lfence
shl rdx, 0x20
or rax, rdx