Fra*_*ger 46 compiler-construction optimization gcc visual-studio likely-unlikely
GCC编译器支持__builtin_expect语句,该语句用于定义可能的和不太可能的宏.
例如.
#define likely(expr) (__builtin_expect(!!(expr), 1))
#define unlikely(expr) (__builtin_expect(!!(expr), 0))
Run Code Online (Sandbox Code Playgroud)
是否有Microsoft Visual C编译器的等效声明,或等效的东西?
小智 20
根据http://www.akkadia.org/drepper/cpumemory.pdf(第57页),即使CPU正确动态预测,使用静态分支预测仍然有意义.原因是如果正确进行静态预测,将更有效地使用L1i缓存.
Dig*_*oss 19
没什么东西跟它一样了.有__assume(),但不要使用它,它是一种不同的优化器指令.
真的,gnu内置包装在宏中的原因是,如果__GNUC__没有定义,你可以自动摆脱它.这些宏没有任何必要的东西,我打赌你不会注意到运行时差.
*likely在非GNU上摆脱(null out).你不会错过它.
小智 14
C++ 20标准将包括[[likely]]和[[unlikely]]分支预测属性.
最新版本的属性提案可以在http://wg21.link/p0479找到
原始属性提案可以在http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html找到.
程序员应该更喜欢PGO.如果应用不正确,属性可以轻松降低性能,或者在程序更改时它们稍后变得不正确
我知道这个问题是关于 Visual Studio 的,但我将尝试回答尽可能多的编译器(包括 Visual Studio)\xe2\x80\xa6
\n十年后,进步了!截至 Visual Studio 2019 MSVC 仍然不支持类似的东西(即使它是最流行的内置/内在),但正如 Pauli Nieminen 上面提到的,C++20 有likely/unlikely属性,可用于创建可能的/不太可能的宏和 MSVC 通常会很快添加对新 C++ 标准的支持(与 C 不同),因此我希望 Visual Studio 2021 能够支持它们。
目前(2019-10-14)只有GCC支持这些属性,甚至只应用于标签,但至少做一些基本的测试就足够了。这是一个快速实现,您可以在 Compiler Explorer 上测试:
\n#define LIKELY(expr) \\\n ( \\\n ([](bool value){ \\\n switch (value) { \\\n [[likely]] case true: \\\n return true; \\\n [[unlikely]] case false: \\\n return false; \\\n } \\\n }) \\\n (expr))\n#define UNLIKELY(expr) \\\n ( \\\n ([](bool value){ \\\n switch (value) { \\\n [[unlikely]] case true: \\\n return true; \\\n [[likely]] case false: \\\n return false; \\\n } \\\n }) \\\n (expr))\nRun Code Online (Sandbox Code Playgroud)\n编辑(2022-05-02):MSVC 2022 支持 C++20,包括[[likely]]/ [[unlikely]],但为此生成了绝对糟糕的代码(请参阅这篇文章的评论)...不要在那里使用它。
您可能需要 #ifdef 来支持无法处理它的编译器,但幸运的是大多数编译器都支持__builtin_expect:
GCC 9+ 还支持__builtin_expect_with_probability. 它在其他地方不可用,但希望有一天\xe2\x80\xa6 尝试弄清楚是否使用 ilkely/不太可能\xe2\x80\x94 需要大量的猜测,你只需设置概率并且编译器(理论上)做了正确的事情。
另外,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 版本,但应该很快就会出现\xe2\x80\xa6
即使您不想在项目中使用 Hedley,您可能也想检查那里的实现,而不是依赖上面的列表;我可能会忘记用新信息更新这个答案,但赫德利应该始终保持最新状态。
\n__assume应该是类似的.
但是,如果你想要做到这一点,你应该使用Profile Guided Optimization而不是静态提示.
根据英特尔的分支和循环重组预防错误预测文件:
为了有效地编写代码以利用这些规则,在编写if-else或switch语句时,首先检查最常见的情况,然后逐步处理最不常见的情况.
不幸的是你不能写类似的东西
#define if_unlikely(cond) if (!(cond)); else
Run Code Online (Sandbox Code Playgroud)
因为VS10的MSVC优化器忽略了这样的"提示".
由于我更喜欢在代码中首先处理错误,因此我似乎编写效率较低的代码.幸运的是,第二次CPU遇到分支时,它将使用其统计信息而不是静态提示.
小智 5
但事实上,使用“可能”和不使用之间没有任何区别。
我已经编译了这些代码并产生相同的结果。
int main()
{
int i = rand() % 2;
if (i) [[likely]]
{
printf("Hello World!\n");
}
else
{
printf("Hello World2%d!\n",i);
}
}
Run Code Online (Sandbox Code Playgroud)
int main()
{
int i = rand() % 2;
if (i)
{
printf("Hello World!\n");
}
else [[likely]]
{
printf("Hello World2%d!\n",i);
}
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16865 次 |
| 最近记录: |