13

由于 VC++ 2010 不支持 64 位代码中的内联汇编,我如何在我的pause代码中获取 x86-64 指令?这似乎没有像许多其他常见的汇编指令(例如,、等)那样的内在__rdtsc()函数__cpuid()

在为什么方面,我希望该指令有助于处理繁忙的等待用例,以便(超线程)CPU 可用于在所述 CPU 上运行的其他线程(请参阅: intel.com 上的Performance Insights)。该pause指令对于这个用例以及自旋锁实现非常有帮助,我不明白为什么 MS 没有将它作为内在函数包含在内。

谢谢

4

2 回答 2

15

哇,这是一个很难追踪的问题,但如果其他人需要 x86-64pause指令:

YieldProcessor()宏 fromwindows.h扩展为未记录的内在_mm_pause函数,最终扩展为pause32 位和 64 位代码中的指令。

顺便说一句,这完全没有记录,在 MSDN 中出现了YieldProcessor()的部分(并且对于 VC++ 2010 文档不正确)。

下面是一个 YieldProcessor() 宏块编译成的示例:

    19:     ::YieldProcessor();
000000013FDB18A0 F3 90                pause  
    20:     ::YieldProcessor();
000000013FDB18A2 F3 90                pause  
    21:     ::YieldProcessor();
000000013FDB18A4 F3 90                pause  
    22:     ::YieldProcessor();
000000013FDB18A6 F3 90                pause  
    23:     ::YieldProcessor();
000000013FDB18A8 F3 90                pause  

顺便说一句,在 Nehalem 架构上,每个暂停指令似乎平均会产生大约 9 个周期的延迟(即,在 3.3 GHz CPU 上为 3 ns)。

于 2011-04-29T14:56:15.653 回答
9

_mm_pause()内在函数由英特尔完整记录,并得到所有主要 x86 编译器的支持,可跨操作系统移植。IDK 如果 MS 的文档过去缺少,或者您只是错过了大约 7 年。

#include <immintrin.h>并使用它。#include <emmintrin.h>(或者对于SSE2的古代编译器)。

#include <immintrin.h>

void test() {
    _mm_pause();
    _mm_pause();
}

在所有 4 个 gcc/clang/ICC/MSVC 上编译成这个 asm(在 Godbolt 编译器资源管理器上):

test():                               # @test()
    pause
    pause
    ret

在没有 SSE2 的 CPU 上,它解码为rep nopwhich is just a nop. x86暂停指令的跨平台实现

Gcc 甚至知道这一点,并且_mm_pause()在使用-mno-sse. (与 MSVC 不同,通常 gcc 和 clang 拒绝 intriniscs 未启用的指令。)有趣的是,gcc 甚至rep nop在其 asm 输出中发出,而其他三个发出pause。当然,它们组装成相同的机器代码。


暂停使该超线程的前端在 Sandybridge 系列上闲置约 5 个周期,直到 Skylake。在 Skylake 上,英特尔将其增加到约 100 个周期,以在自旋等待循环中节省更多功率,并以可能的延迟为代价提高整体吞吐量,尤其是在超线程内核上。

在所有 CPU 上,它还避免了离开自旋循环时的内存顺序错误推测。因此,当它最终再次重要时,它确实减少了延迟。

另请参阅x86 中“PAUSE”指令的目的是什么?.

于 2018-08-18T12:41:36.540 回答