多个 nop 指令并不总是比单个 nop 指令花费更长的时间

pic*_*ard 4 assembly processor inline-assembly rdtsc no-op

我使用 C++ 对多个 NOP 指令和单个 NOP 指令进行计时rdtsc。但是,我没有发现执行 NOP 所需的周期数与执行的 NOP 数成正比。我很困惑为什么会出现这种情况。我的 CPU 是 Intel Core i7-5600U @ 2.60Ghz。

这是代码:

#include <stdio.h>

int main() {
    unsigned long long t;

    t = __rdtsc();
    asm volatile("nop");
    t = __rdtsc() - t;
    printf("rdtsc for one NOP: %llu\n", t);

    t = __rdtsc();
    asm volatile("nop; nop; nop; nop; nop; nop; nop;");
    t = __rdtsc() - t;
    printf("rdtsc for seven NOPs: %llu\n", t);

}
Run Code Online (Sandbox Code Playgroud)

我得到的值如下:

rdtsc for one NOP: 78
rdtsc for seven NOPs: 91

rdtsc for one NOP: 78
rdtsc for seven NOPs: 78
Run Code Online (Sandbox Code Playgroud)

在未设置处理器关联的情况下运行时。当设置处理器关联性时,如$ taskset -c 0 ./nop$,结果是:

rdtsc for one NOP: 78
rdtsc for seven NOPs: 78

rdtsc for one NOP: 130
rdtsc for seven NOPs: 169

rdtsc for one NOP: 78
rdtsc for seven NOPs: 143
Run Code Online (Sandbox Code Playgroud)

为什么会出现这样的情况呢?

Pet*_*des 5

printf这里的结果可能是测量噪声和/或频率缩放,因为您在系统调用返回 后立即启动第二个间隔的计时器。

RDTSC 计算参考周期,而不是核心时钟周期,因此您主要只是发现 CPU 频率。(较低的核心时钟速度=相同数量的核心时钟运行两条rdtsc指令需要更多的参考周期)。您的 RDTSC 指令基本上是背靠背的;nop与本身解码的微指令数量相比rdtsc(在包括 Broadwell 在内的普通 CPU 上),这些指令可以忽略不计。

RDTSC 也可以通过乱序执行重新排序。这并不是nopCPU 必须等待的事情;它只是将前端发出 2nd 的 uop 延迟了 0.25 或 1.75 个周期rdtsc。(实际上,我不确定微码定序器是否可以在与来自另一条指令的微指令相同的周期中发送微指令。所以可能是 1 或 2 个周期)。

我对How to get the CPU Cycle count in x86_64 from C++? 的回答 有很多关于 RDTSC 如何工作的背景知识。


您可能需要pause说明。在 Skylake 及更高版本上,它空闲约 100 个核心时钟周期,在早期 Intel 核心上空闲约 5 个周期。 或者旋转 PAUSE + RDTSC如何计算 x86 linux 上 asm 延迟循环的时间?显示了一个可能有用的延迟自旋循环,它会休眠给定数量的 RDTSC 计数。您需要知道参考时钟速度才能将其与纳秒相关联,但它通常约为英特尔 CPU 上的额定最大非涡轮时钟。例如,4.0GHz Skylake 上的 4008 MHz。

如果可用,tpause则采用 TSC 时间戳作为唤醒时间。(参见链接)。但目前还只是低功耗 Tremont。


在具有巨大重排序缓冲区的现代超标量/乱序 x86 上插入 NOP 永远无法可靠地工作!现代 x86 不是可以计算嵌套延迟循环迭代的微控制器。如果周围的代码不会在前端造成瓶颈,OoO exec 只是会隐藏通过管道提供 NOP 的成本。

说明没有成本,您只需累加即可。要对指令的成本进行建模,您需要了解其延迟、前端 uop 计数以及它需要哪些后端执行端口。以及管道上的任何特殊效果,例如lfence等待所有先前的微指令退出,然后才能发出后续微指令。 每条汇编指令需要多少个CPU周期?

另请参阅预测现代超标量处理器上的操作延迟需要考虑哪些因素以及如何手动计算它们?


请注意,如果运行中存在缓存未命中,或者甚至可能是非常慢的 ALU 依赖链,则您所需的约 100 纳秒的“睡眠”时间不一定足够长以耗尽无序执行缓冲区 (ROB)。(除了人为情况之外,后者不太可能)。所以你可能不想做类似的事情lfence