多个nop指令的执行时间不一定比单个nop指令的时间长

时间:2019-10-15 01:30:15

标签: assembly inline-assembly processor rdtsc nop

我正在使用rdtsc在C ++中定时多个NOP指令和单个NOP指令。但是,执行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);

}

我得到的值如下:

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

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

在未设置处理器关联性的情况下运行。 像$ 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

为什么会这样?

1 个答案:

答案 0 :(得分:3)

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

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

此外,RDTSC可以通过乱序执行来重新排序。并不是说nop会做CPU需要等待的任何事情。它只是从发出第二个rdtsc的微指令开始就将前端延迟了0.25或1.75个周期。 (实际上,我不确定微代码定序器是否可以在与另一个指令的uop相同的周期内发送uops。因此可能是1或2个周期。)

我对How to get the CPU cycle count in x86_64 from C++?的回答对RDTSC的工作原理有很多了解。


您可能需要pause指令。在Skylake和更高版本上,它空闲约100个内核时钟周期,而在较早的Intel内核上,它空闲约5个周期。 或启用PAUSE + RDTSC How to calculate time for an asm delay loop on x86 linux? 显示了可能有用的延迟自旋循环,该循环在给定数量的RDTSC计数下进入睡眠状态。您需要知道参考时钟速度,才能将其与纳秒关联,但是通常在Intel CPU上的额定最大非涡轮增压时钟附近。例如在4.0GHz Skylake上为4008 MHz。

如果可用,tpause将TSC时间戳记作为唤醒时间。 (请参阅链接)。但这只是目前的低功率Tremont。


在具有超大重排序缓冲区的现代超标量/无序x86上可靠地插入NOP永远不会起作用!现代的x86并非微控制器,您可以在其中计算嵌套延迟循环的迭代次数。如果周围的代码在前端没有瓶颈,那么OoO执行人员将隐藏通过管道提供NOP的成本。

说明没有费用,您只需累加即可。要对一条指令的成本进行建模,您需要知道其延迟,前端uop计数以及所需的后端执行端口。还有对管道的任何特殊影响,例如lfence等待所有先前的微指令退役,然后再发布。 How many CPU cycles are needed for each assembly instruction?

另请参阅What considerations go into predicting latency for operations on modern superscalar processors and how can I calculate them by hand?


请注意,如果正在运行的高速缓存未命中,或者可能是非常缓慢的ALU,则所需的〜100ns的“睡眠”时间不一定足够长以耗尽乱序执行缓冲区(ROB)。依赖链。 (在人工案例之外,后者不太可能)。因此,您可能不想做类似lfence的事情。