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。如果不正确地应用或者程序变化后它们变得不正确,这些属性很容易降低性能。
__builtin_expect
(又称 likely
/unlikely
)的东西?不实现此功能的论点是它是非标准的。MSVC正在推动实现标准特性,而不是以与其他编译器不兼容的方式扩展语言。(我们在过去做了太多这样的事情。)已经有一个标准提案引入了这样的属性。当它被标准化后,我们将实现它:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0627r0.pdf - phuclv没有什么可以替代它。虽然有__assume(),但不要使用它,因为它是一种不同类型的优化器指令。
实际上,gnu内置函数被包装在宏中只是为了让你可以自动删除它,如果未定义 __GNUC__
。这些宏并没有什么必要性,我敢打赌你不会注意到运行时差异。
在非GNU环境下,只需删除(将* likely设置为空)即可。你不会感觉到它的缺失。
safeCall(mDevice.doit())
这样的检查,我的 safeCall
函数被内联了,这可以提高性能,但只有在我有可能/不可能分支时才能使用。我想说这些宏可能很有用。 - Mikhailunlikely
内置函数。没有 PGO(这是个大烦恼),愚蠢的 MSVC 几乎总是把指令排序错了。 - rustyx_assume
命名为“close”有点不太合适,因为它根本不相似。不了解的读者可能会错误地解释“可以获得错误的代码”。它的含义等同于 __builtin_unreachable
。将其用于分支提示不仅是危险的,而且在任何情况下都是不正确的。 - Damon我知道这个问题是关于Visual Studio的,但我会尝试为尽可能多的编译器(包括Visual Studio)回答……
十年后终于有所进展!截至到Visual Studio 2019,MSVC仍然不支持任何类似的功能(即使它是最受欢迎的内置/内建函数),但正如Pauli Nieminen上面提到的,C++20具有likely
/unlikely
属性,可以用于创建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))
编辑(2022-05-02):MSVC 2022支持C++20,包括[[likely]]
/[[unlikely]]
,但对此生成的代码非常糟糕(请参见本帖子中的评论)...不要在那里使用它。__builtin_expect
:
__builtin_expect_with_probability
。虽然它只在这里可用,但希望有一天会…它可以消除尝试确定是否使用likely/unlikely时的很多猜测 - 您只需设置概率,编译器(理论上)即可正确处理。__builtin_unpredictable
(自3.8以来),但使用__has_builtin(__builtin_unpredictable)
进行测试。由于许多编译器现在都是基于clang的,因此它可能也适用于它们。HEDLEY_LIKELY
、HEDLEY_UNLIKELY
、HEDLEY_UNPREDICTABLE
、HEDLEY_PREDICT
、HEDLEY_PREDICT_TRUE
和HEDLEY_PREDICT_FALSE
。虽然它还没有 C++20 版本,但很快就会有...
即使您不想在项目中使用 Hedley,您也可能想检查那里的实现,而不是依赖于上面列出的列表;我可能会忘记更新此答案的新信息,但 Hedley 应该始终是最新的。
[[likely]]
/[[unlikely]]
可以工作,我会尽快更新Hedley。 - nemequ__builtin_expect
),所以根本不需要更新它。我会在这个回答中加上一个说明,并尝试记得向微软提交问题,但我不指望能得到解决:( - nemequ#define if_unlikely(cond) if (!(cond)); else
__assume应该类似。
然而,如果您想做得更好,您应该使用Profile Guided Optimization 而不是静态提示。
但事实上,使用"可能"或不使用没有任何区别。
我已经编译了这些代码,产生了相同的结果。
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
[[likely]]
/[[unlikely]]
或者没有影响的答案已经过时了。/std:c++20
和/std:c++latest
模式下使用[[likely]]
/[[unlikely]]
。if-else
语句在x86/x64上的一个明显效果是条件跳转将会是不太可能的分支。在C++20之前并且支持VS版本之前,可以通过将可能的分支放入if
部分,将不太可能的分支放入else
部分,并根据需要取反条件来实现相同的效果。
BOOST_LIKELY
和BOOST_UNLIKELY
。 - phuclv