由于VC++ 2010在64位代码中不支持内联汇编,那么我该如何将 pause
x86-64指令添加到我的代码中?似乎没有像其他常见的汇编指令(例如__rdtsc()
、__cpuid()
等)那样的内置函数。
为什么要使用这个指令呢?我想将其用于繁忙等待的情况下,这样(超线程的)CPU就可以为运行在该CPU上的其他线程提供服务(请参阅:intel.com上的性能见解)。pause
指令对于这种用例以及自旋锁实现非常有帮助,我无法理解微软为什么没有将其作为内置函数。
谢谢
由于VC++ 2010在64位代码中不支持内联汇编,那么我该如何将 pause
x86-64指令添加到我的代码中?似乎没有像其他常见的汇编指令(例如__rdtsc()
、__cpuid()
等)那样的内置函数。
为什么要使用这个指令呢?我想将其用于繁忙等待的情况下,这样(超线程的)CPU就可以为运行在该CPU上的其他线程提供服务(请参阅:intel.com上的性能见解)。pause
指令对于这种用例以及自旋锁实现非常有帮助,我无法理解微软为什么没有将其作为内置函数。
谢谢
哇,这个问题很难追踪,但如果有其他人需要x86-64 pause
指令,可以参考以下内容:
windows.h
中的 YieldProcessor()
宏会展开为未记录的 _mm_pause
内置函数,该函数最终会在32位和64位代码中展开为 pause
指令。
顺便提一句,这完全没有记录,部分记录(对于 VC++ 2010 文档不正确)可在 MSDN 的 YieldProcessor() 中找到。
以下是一段使用 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)。
_mm_pause()
内置函数已经被英特尔充分文档化,并且在跨操作系统的所有主要x86编译器中得到支持。我不知道微软过去的文档是否有所欠缺,或者你只是错过了7年前的内容。
#include <immintrin.h>
并使用它。(或对于古老的编译器,使用#include <emmintrin.h>
进行SSE2)。
#include <immintrin.h>
void test() {
_mm_pause();
_mm_pause();
}
在gcc/clang/ICC/MSVC上编译后,会生成以下汇编代码(在Godbolt编译器探索器上):
test(): # @test()
pause
pause
ret
rep nop
,只是一个nop
。跨平台实现x86暂停指令
Gcc甚至知道这一点,在使用-mno-sse
编译时仍然接受_mm_pause()
。(通常,与MSVC不同,gcc和clang会拒绝未启用的指令的内部函数。)有趣的是,gcc甚至在其汇编输出中发出rep nop
,而其他三个则发出pause
。它们当然组装成相同的机器代码。
在Sandybridge系列到Skylake之前,暂停会使超线程的前端空闲约5个周期。 在Skylake上,Intel将其增加到大约100个周期,以在自旋等待循环中节省更多电力并增加总吞吐量,可能以延迟为代价,特别是在超线程核心上。
在所有CPU上,当退出自旋循环时,它还可以避免内存序错误推测。 因此,它确实在最终再次重要时减少了延迟。
另请参见x86中“PAUSE”指令的目的是什么?。
__yield
被记录时)。有时候,最好只是查找intrin.h中与您所需名称类似的名称(这就是我发现_mm_pause
的方式),尽管您的宏似乎更适合可移植性,+1。 - Necrolis