c - RDTSCP 与 RDTSC + CPUID

标签 c assembly linux-kernel x86

我正在做一些 Linux 内核计时,特别是在中断处理路径中。我一直在使用 RDTSC 进行计时,但我最近了解到它不一定准确,因为指令可能会乱序发生。

然后我尝试了:

  1. RDTSC + CPUID(此处为相反顺序)刷新管道,并且由于 super 调用在虚拟机(我的工作环境)上产生高达 60 倍的开销(!)什么的。这包括启用和不启用硬件虚拟化。

  2. 最近我遇到了 RDTSCP* 指令,它似乎执行 RDTSC+CPUID 所做的事情,但效率更高,因为它是一条较新的指令 - 相对而言只有 1.5 到 2 倍的开销。

我的问题:作为一个测量点,RDTSCP 是否真的准确,它是否是“正确”的计时方式?

还要说得更清楚,我的时间安排基本上是这样的,在内部:

  • 保存当前循环计数器值
  • 执行一种基准测试(即:磁盘、网络)
  • 将当前和上一个循环计数器的增量添加到累加器值并递增计数器,每个单独的中断
  • 最后,将增量/累加器除以中断次数,得到每次中断的平均周期成本。

* http://www.intel.de/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf第 27 页

最佳答案

有关您从 cpuid 指令中看到的开销的完整讨论可在 this stackoverflow thread 找到。 .使用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);

根据读取发生的位置,您的平均测量值可能仍有偏差。例如:

   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”是否指的是可以以这种方式比赛的时间。如果是这样,您可能需要计算与增量一致的移动平均值或移动平均值。

侧点:

  1. 如果您确实使用 cpuid+rdtsc,则需要减去 cpuid 指令的成本,这可能很难确定您是否在 VM 中(取决于 VM 如何实现此指令)。这就是您应该坚持使用 rdtscp 的真正原因。
  2. 在循环中执行 rdtscp 通常不是一个好主意。我经常看到微基准测试做类似的事情

--

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));

虽然这会让您对 loop_body() 中的任何内容在循环中的整体性能有一个不错的了解,但它会破坏处理器优化,例如流水线。在微基准测试中,处理器会在循环中很好地进行分支预测,因此可以测量循环开销。以上面显示的方式执行此操作也很糟糕,因为每次循环迭代最终会出现 2 个管道停顿。因此:

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));

就您在现实生活中看到的内容与之前的基准测试告诉您的内容而言,将更高效,也可能更准确。

关于c - RDTSCP 与 RDTSC + CPUID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27693145/

相关文章:

c - 基指针和栈指针到底是什么?他们指向什么?

linux - 我如何计算出 BPF 辅助函数的返回码的含义?

linux - 如何找出哪个内核自旋锁占用了大部分CPU?

复制到用户 : treating more data

c - 如何知道linux内核在哪里解析tuntap接口(interface)上的MLD连接?

c - 如何通过解析C程序并将其转换为要显示的电路图来反向建模

在 C 中比较浮点值

c - 将 FOR 翻译成汇编程序

c - AMD64——nopw 汇编指令?

c - 错误: null parameter when calling GRBloadmodel