【问题标题】:Why is nop not taking one clock cycle为什么 nop 不占用一个时钟周期
【发布时间】:2020-04-29 04:42:21
【问题描述】:

我编写了一个基本代码来找出 nop 占用的时钟周期数。我们知道 nop 需要一个时钟周期。

#include <stdio.h>
#include <string.h>
#include <stdint.h>


int main(void)
{
    uint32_t low1, low2, high1, high2;
    uint64_t timestamp1, timestamp2;
    asm volatile ("rdtsc" : "=a"(low1), "=d"(high1));
    asm("nop");
    asm volatile ("rdtsc" : "=a"(low2), "=d"(high2));
    timestamp1 = ((uint64_t)high1 << 32) | low1; 
    timestamp2 = ((uint64_t)high2 << 32) | low2; 
    printf("Diff:%lu\n", timestamp2 - timestamp1);
    return 0;
}

但是输出不是1。

有时是 14 或 16。

我可以知道这背后的原因吗?我有什么遗漏吗

【问题讨论】:

  • 一方面,您的计时循环还包括执行 1 个rdtsc 指令的时间。
  • 你怎么知道nop需要一个周期?一些处理器可以在每个周期从指令流中删除几个,因此它们永远不会被分派并且不会消耗执行时间。
  • @LegendofPedro: 不,-O0 只会在时间间隔内放入更多垃圾,但仍然不会让第二个 RDTSC 等待先前指令的完成。或者也停止第一个 RDTSC 的运行。请参阅我关于 RDTSC 的规范答案:How to get the CPU cycle count in x86_64 from C++?
  • 我们知道 nop 需要一个时钟周期。 What kind of chip you got in there, a Dorito? 说真的,你在什么 CPU 上测试过这个,所以答案可以包括假设constant_tsc,基本/参考频率(TSC)与实际核心时钟周期的详细说明?肯定不是 486 或更早的 NOP 实际上会花费 1 个周期。
  • @LegendofPedro:没错,你想要优化 asm。不,GCC/clang 不“理解” asm 模板,他们只在将结果(包括编译器生成的 asm)提供给汇编器之前扫描它以查找 "%number" 操作数替换。使用-O0,您可以将low1low2 的存储空间存储到堆栈空间,而不仅仅是mov 存储到其他寄存器(或者可能shl/lea 存储到第二个rdtsc 之前的另一个寄存器中)。第二次看,您实际上不会在-O0 的(尝试a)“定时区域”中获得更多指令,因为没有什么可以溢出/重新加载;第二个 asm 没有输入

标签: c x86 inline-assembly rdtsc cpu-cycles


【解决方案1】:

我们知道 nop 需要一个时钟周期。

现代 CPU 可以被认为是一个阶段的流水线;前端可能会并行获取和解码多个指令,并将生成的微操作放入缓冲区中,等待它们的依赖关系得到满足(在被执行单元获取之前,可以在其中执行多个微操作)多个执行单元同时执行)。

NOP 没有微操作 - 它只是被前端丢弃。它不需要 1 个周期。

但是输出不是1。

编译器生成的指令可能需要 14 或 16 个周期来处理第​​一个 rdtsc 的输出,然后为第二个 rdtsc 准备东西,然后是第二个 rdtsc 本身。

请注意,rdtsc 可能会计算一个固定频率计时器的周期,该计时器没有 CPU 的当前(可变)时钟频率;所以 14 或 16 个“时间周期”可能是(例如)7 或 8 个 CPU 周期。

【讨论】:

  • 14 个周期对于背靠背 rdtsc 来说实际上是很低的(中间只有一个 mov ecx, eax)。请注意,rdtsc 在执行之前等待先前的指令完成执行,并且它没有输入,因此它的微代码可以在有空闲执行单元时立即开始执行。
  • Skylake RDTSC 吞吐量是每 24 个内核时钟周期 (agner.og/optimize) 一个,而 Ryzen 是 36 个周期。因此,OP 的 CPU 可能会显着高于 TSC 的“参考”频率。除非它是空闲的 K8,尽管 K8 可能没有constant_tsc。无论如何,有关rdtsc的更多详细信息,另请参阅How to get the CPU cycle count in x86_64 from C++?
  • 回复:nop 的成本:如果前端没有瓶颈,则无需任何成本,否则可能会使解码 + 发出一组指令的总成本增加 1/循环的 4 或 1/5,或者更多,如果它以有问题的方式导致不同的对齐方式。不过,它实际上并没有被前端丢弃。它在 ROB 中占用一个空间(1 个融合域 uop),但不需要执行单元(0 个未融合)。您可以将其想象为前端以“已执行”状态将其插入后端,例如消除了 mov 和(在 Sandybridge-family 上)xor-zeroing。
  • 执行 NOP 并不是一个足够重要的性能问题,值得在前端更早地对其进行特殊封装以节省前端问题带宽。我认为实际上这样做会使一堆极端情况变得复杂,即使我们接受性能计数器不再计算它。
  • constant_tsc 存在于 /proc/cpuinfo 中。我在 vmware 上运行它
猜你喜欢
  • 2017-09-24
  • 2017-03-08
  • 2019-04-01
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
相关资源
最近更新 更多