GCC 编译器支持 __builtin_expect 语句,用于定义可能和不太可能的宏。
例如。
#define likely(expr) (__builtin_expect(!!(expr), 1))
#define unlikely(expr) (__builtin_expect(!!(expr), 0))
是否有 Microsoft Visual C 编译器的等效语句或等效语句?
GCC 编译器支持 __builtin_expect 语句,用于定义可能和不太可能的宏。
例如。
#define likely(expr) (__builtin_expect(!!(expr), 1))
#define unlikely(expr) (__builtin_expect(!!(expr), 0))
是否有 Microsoft Visual C 编译器的等效语句或等效语句?
C++20 标准将包含[[likely]]
和[[unlikely]]
分支预测属性。
最新版本的属性提案可以从http://wg21.link/p0479找到
原始属性提案可以从http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html找到
程序员应该更喜欢 PGO。如果应用不正确,属性很容易降低性能,或者在程序更改后它们会变得不正确。
根据http://www.akkadia.org/drepper/cpumemory.pdf(第 57 页),即使 CPU 动态正确预测,使用静态分支预测仍然有意义。原因是如果静态预测正确,L1i 缓存的使用效率会更高。
没什么东西跟它一样了。有__assume(),但不要使用它,它是一种不同类型的优化器指令。
__GNUC__
实际上,gnu 内置函数被包装在宏中的原因是,如果未定义,您可以自动摆脱它。这些宏没有任何必要,我敢打赌你不会注意到运行时间差异。
只需摆脱*likely
非 GNU 上的 (null out) 即可。你不会错过的。
根据 Intel 的Branch and Loop Reorganization to prevent Mispredicts文件:
为了有效地编写代码以利用这些规则,在编写 if-else 或 switch 语句时,首先检查最常见的情况,然后逐步降低到最不常见的情况。
不幸的是,你不能写像
#define if_unlikely(cond) if (!(cond)); else
因为从 VS10 开始的 MSVC 优化器会忽略这样的“提示”。
由于我更喜欢首先在代码中处理错误,因此我似乎编写的代码效率较低。幸运的是,CPU 第二次遇到分支时,它将使用其统计信息而不是静态提示。
__assume应该是相似的。
但是,如果您想很好地做到这一点,您应该使用Profile Guided Optimization而不是静态提示。
我知道这个问题是关于 Visual Studio 的,但我会尽量回答尽可能多的编译器(包括 Visual Studio)……</p>
十年后有进步!截至 Visual Studio 2019 MSVC 仍然不支持这样的东西(即使它是最流行的 builtin/intrinsic),但正如 Pauli Nieminen 上面提到的 C++20 具有likely
/unlikely
属性,可用于创建可能/不太可能的宏和MSVC 通常会很快添加对新 C++ 标准的支持(与 C 不同),因此我希望 Visual Studio 2021 能够支持它们。
目前(2019-10-14)只有 GCC 支持这些属性,即使那时也只应用于标签,但至少做一些基本的测试就足够了。这是一个快速实现,您可以在 Compiler Explorer 上进行测试:
#define LIKELY(expr) \
( \
([](bool value){ \
switch (value) { \
[[likely]] case true: \
return true; \
[[unlikely]] case false: \
return false; \
} \
}) \
(expr))
#define UNLIKELY(expr) \
( \
([](bool value){ \
switch (value) { \
[[unlikely]] case true: \
return true; \
[[likely]] case false: \
return false; \
} \
}) \
(expr))
您可能希望围绕它使用#ifdef 来支持无法处理它的编译器,但幸运的是大多数编译器都支持__builtin_expect
:
GCC 9+ 还支持__builtin_expect_with_probability
. 它在其他任何地方都不可用,但希望有朝一日......试图弄清楚是否使用 ilkely/unlikely 需要大量的猜测 - 你只需设置概率并且编译器(理论上)会做正确的事情。
此外,clang 支持 a __builtin_unpredictable
(从 3.8 开始,但使用 测试它__has_builtin(__builtin_unpredictable)
)。由于现在很多编译器都是基于 clang 的,所以它也可能在它们中工作。
如果您希望这一切都结束并准备就绪,您可能会对我的一个项目Hedley感兴趣。它是一个单一的公共域 C/C++ 头文件,几乎适用于所有编译器,并包含许多有用的宏,包括HEDLEY_LIKELY
、HEDLEY_UNLIKELY
、HEDLEY_UNPREDICTABLE
、HEDLEY_PREDICT
、HEDLEY_PREDICT_TRUE
和HEDLEY_PREDICT_FALSE
. 它还没有 C++20 版本,但应该很快就会出现……</p>
即使你不想在你的项目中使用 Hedley,你也可能想要检查那里的实现而不是依赖上面的列表;我可能会忘记用新信息更新这个答案,但 Hedley 应该始终是最新的。
现在 MS说他们已经实现了可能/不太可能的属性
但实际上使用“可能”与不使用并没有什么不同。
我已经编译了这些代码并产生了相同的结果。
int main()
{
int i = rand() % 2;
if (i) [[likely]]
{
printf("Hello World!\n");
}
else
{
printf("Hello World2%d!\n",i);
}
}
int main()
{
int i = rand() % 2;
if (i)
{
printf("Hello World!\n");
}
else [[likely]]
{
printf("Hello World2%d!\n",i);
}
}
int pdb._main (int argc, char **argv, char **envp);
0x00401040 push ebp
0x00401041 mov ebp, esp
0x00401043 push ecx
0x00401044 call dword [rand] ; pdb.__imp__rand
; 0x4020c4
0x0040104a and eax, 0x80000001
0x0040104f jns 0x401058
0x00401051 dec eax
0x00401052 or eax, 0xfffffffe ; 4294967294
0x00401055 add eax, 1
0x00401058 je 0x40106d
0x0040105a push str.Hello_World ; pdb.___C__0O_NFOCKKMG_Hello_5World__CB_6
; 0x402108 ; const char *format
0x0040105f call pdb._printf ; int printf(const char *format)
0x00401064 add esp, 4
0x00401067 xor eax, eax
0x00401069 mov esp, ebp
0x0040106b pop ebp
0x0040106c ret
0x0040106d push 0
0x0040106f push str.Hello_World2_d ; pdb.___C__0BB_DODJFBPJ_Hello_5World2__CFd__CB_6
; 0x402118 ; const char *format
0x00401074 call pdb._printf ; int printf(const char *format)
0x00401079 add esp, 8
0x0040107c xor eax, eax
0x0040107e mov esp, ebp
0x00401080 pop ebp
0x00401081 ret
由于这个问题很老,所以说 MSVC 中没有[[likely]]
/[[unlikely]]
或没有影响的答案已经过时了。
最新的 MSVC 支持[[likely]]
/ [[unlikely]]
in/std:c++20
和/std:c++latest
模式。
请参阅Godbolt 的编译器资源管理器上的演示,其中显示了差异。
As can be seen from the link above, one visible effect on x86/x64 for if-else
statement is that the conditional jump forward will be for unlikely branch. Before C++20 and supporting VS version the same could be achieved by placing the likely branch into if
part, and the unlikely branch into else
part, negating the condition as needed.
Note that the effect of such optimization is minimal. For frequently called code in a tight loop, the dynamic branch prediction would do the right thing anyway.