问题 如何在64位C ++代码中使用暂停汇编指令?


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

在为什么这一方面,我希望指令帮助忙碌的等待用例,以便(超线程)CPU可用于在所述CPU上运行的其他线程(参见: 绩效见解 在intel.com)。该 pause 指令对于这个用例以及自旋锁实现非常有用,我无法理解为什么MS没有将它作为内在包含。

谢谢


4500
2018-04-29 14:42


起源



答案:


哇,这是一个非常难以追查的问题,但万一其他人需要x86-64 pause 指令:

YieldProcessor() 来自的宏 windows.h 扩展到无证件 _mm_pause 内在的,最终扩展到 pause 32位和64位代码指令。

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

以下是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)。


14
2018-04-29 14:56



不幸的是,MS'忘记'记录了很多功能,这很烦人(特别是当 __yield 记录在案)。有时候,只需梳理一下intrin.h就可以找到与你的名字类似的名字(这就是我的发现方式 _mm_pause虽然你的宏看起来好像便携性,但+1) - Necrolis
__yield可以记录,但它只适用于IA64(即,不是任何人们实际使用的架构:))。当我发现它时,我真的非常高兴,但却发现它是#ifdefed用于x86 / x86-64版本。 - Michael Goldshteyn


答案:


哇,这是一个非常难以追查的问题,但万一其他人需要x86-64 pause 指令:

YieldProcessor() 来自的宏 windows.h 扩展到无证件 _mm_pause 内在的,最终扩展到 pause 32位和64位代码指令。

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

以下是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)。


14
2018-04-29 14:56



不幸的是,MS'忘记'记录了很多功能,这很烦人(特别是当 __yield 记录在案)。有时候,只需梳理一下intrin.h就可以找到与你的名字类似的名字(这就是我的发现方式 _mm_pause虽然你的宏看起来好像便携性,但+1) - Necrolis
__yield可以记录,但它只适用于IA64(即,不是任何人们实际使用的架构:))。当我发现它时,我真的非常高兴,但却发现它是#ifdefed用于x86 / x86-64版本。 - Michael Goldshteyn


_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 nop 这只是一个 nop跨平台实现x86暂停指令

Gcc甚至知道这一点,仍然接受 _mm_pause() 编译时 -mno-sse。 (正常情况下,gcc和clang拒绝使用未启用指令的内容,与MSVC不同。)有趣的是,gcc甚至会发出 rep nop 在其asm输出中,而其他三个发出 pause。当然,它们组装成相同的机器代码。


在Sandybridge家族停留超过5个周期之前暂停闲置,直到Skylake。在Skylake上,英特尔将其增加到约100个周期,以便在自旋等待循环中节省更多功率。

也可以看看 x86中“PAUSE”指令的目的是什么?


0
2017-08-18 12:41