【问题标题】:solution to rdtsc out of order execution?rdtsc乱序执行的解决方案?
【发布时间】:2019-07-08 11:56:33
【问题描述】:

我正在尝试用 rdtsc 替换 clock_gettime(CLOCK_REALTIME, &ts) 以根据 CPU 周期而不是服务器时间来基准代码执行时间。基准测试代码的执行时间对软件至关重要。我曾尝试在 x86_64 3.20GHz ubuntu 机器上的独立内核上运行代码并得到以下数字:

案例 1:时钟获取时间: 24 纳秒

void gettime(Timespec &ts) {
        clock_gettime(CLOCK_REALTIME, &ts);
}

案例 2:rdtsc(没有 mfence 和编译器屏障): 10 ns

void rdtsc(uint64_t& tsc) {
        unsigned int lo,hi;
        __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
        tsc = ((uint64_t)hi << 32) | lo;
}

案例 3:rdtsc(带有 mfence 和编译器屏障): 30 ns

void rdtsc(uint64_t& tsc) {
        unsigned int lo,hi;
        __asm__ __volatile__ ("mfence;rdtsc" : "=a" (lo), "=d" (hi) :: "memory");
        tsc = ((uint64_t)hi << 32) | lo;
}

这里的问题是我知道 rdtsc 是一个非序列化调用,可以由 CPU 重新排序,另一种选择是 rdtscp,它是一个序列化调用,但 rdtscp 调用之后的指令可以在 rdtscp 调用之前重新排序。使用内存屏障会增加执行时间。

  • 对延迟敏感代码进行基准测试的最优化和最佳方法是什么?
  • 有没有优化我提到的案例?

【问题讨论】:

  • 您可能想看看 Google Test 如何进行分析。

标签: c++ gcc cpu-architecture memory-barriers rdtsc


【解决方案1】:

您希望lfence;rdtsc 启动时钟,并希望rdtscp;lfence 停止时钟,因此障碍在时间间隔之外。

(或者有时您希望lfence;rdtsc;lfence 启动时钟,以增加开销为代价获得额外的可重复性。)

MFENCE 是错误的指令;不能保证序列化指令流(但实际上它在 Skylake 上使用最新的微码来修复错误)。 LFENCE 序列化指令流而不等待存储缓冲区清空,只为 ROB。在 Intel 上总是如此,但在 AMD 上,只有启用 Spectre 缓解才能使lfence 不仅仅是 NOP。 (我猜 AMD 不会重新排序来自 WC 内存的 movntdqa 负载,因此 lfence 在那里作为内存屏障毫无意义,并且可用作针对推测执行或 RDTSC 的执行屏障.)

另见Get CPU cycle count?,其中有一节关于序列化rdtsc。而且,你不需要内联汇编;使用__rdtsc()_mm_lfence()。 (但像往常一样使用微基准测试,检查编译器的 asm 输出以确保它符合您的要求并不是一个坏主意。)


您无法避免开销,与几条指令的成本相比,开销总是很大。

另外clflush to invalidate cache line via C function 是减去测量开销的示例。

但也请注意,通常将要测试的代码置于循环中更有用,因为在结果准备好之前的执行延迟比等待指令实际从 ROB 中退出更有意义。有关测量单个 insn 的吞吐量/延迟的示例(以 asm 格式),请参阅 RDTSCP in NASM always returns the same value

【讨论】:

    猜你喜欢
    • 2013-03-05
    • 2018-02-17
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    • 2020-05-02
    • 2019-02-25
    • 1970-01-01
    相关资源
    最近更新 更多