GCC编译器支持__builtin_expect语句,用于定义可能的和不太可能的宏。
例如
#define likely(expr) (__builtin_expect(!!(expr), 1))
#define unlikely(expr) (__builtin_expect(!!(expr), 0))
是否有Microsoft Visual C编译器的等效声明或等效声明?
答案 0 :(得分:21)
根据http://www.akkadia.org/drepper/cpumemory.pdf(第57页),即使CPU正确动态预测,使用静态分支预测仍然有意义。 这样做的原因是,如果静态预测正确完成,L1i缓存将更有效地使用。
答案 1 :(得分:19)
没有类似的东西。有__assume()但不使用它,它是一种不同的优化器指令。
真的,gnu内置包装在宏中的原因是,如果未定义__GNUC__
,您可以自动删除它。对于那些宏没有任何必要的东西,我打赌你不会注意到运行时差。
在非GNU上摆脱(null out)*likely
。你不会错过的。
答案 2 :(得分:11)
C ++ 20标准将包含public static String userReverse (String userEntry3) {
String reverse = "";
for (int i = (userEntry3.length() -1); i >= 0 ; i--) {
reverse += userEntry3.charAt(i).toString();
}
return reverse;
}
和[[likely]]
分支预测属性。
可以从http://wg21.link/p0479中找到属性建议的最新版本
原始属性建议可在http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html
中找到程序员应该更喜欢PGO。如果应用不正确,属性很容易降低性能,否则当程序更改时,属性会变得不正确。
答案 3 :(得分:6)
__assume应该类似。
但是,如果你想要做得很好,你应该使用Profile Guided Optimization而不是静态提示。
答案 4 :(得分:6)
根据英特尔的Branch and Loop Reorganization to Prevent Mispredicts文件:
为了有效地编写代码以利用这些代码 规则,在编写if-else或switch语句时,请检查最多 常见的情况首先发生,并逐渐减少到最不常见的情况。
不幸的是你不能写像
这样的东西#define if_unlikely(cond) if (!(cond)); else
因为VS10的MSVC优化器忽略了这样的“提示”。
由于我更喜欢在代码中首先处理错误,因此我似乎编写效率较低的代码。 幸运的是,第二次CPU遇到分支时,它将使用其统计信息而不是静态提示。
答案 5 :(得分:1)
我知道这个问题与Visual Studio有关,但是我将尝试尽可能多地回答编译器(包括Visual Studio)…
十年后有了进步!截至Visual Studio 2019,MSVC仍不支持此类功能(即使它是the most popular builtin/intrinsic),但是正如Pauli Nieminen所述,C ++ 20具有likely
/ unlikely
attributes,可用于创建可能/不太可能的内容。宏和MSVC通常会很快添加对新C ++标准的支持(与C不同),因此我希望Visual Studio 2021能够支持它们。
当前(2019-10-14)只有GCC支持这些属性,即使仅将其应用于标签,但至少足以进行一些基本测试。这是您可以test on 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
。它在其他任何地方都无法使用,但希望有一天……试图弄清楚是否不恰当地/不太可能地使用它需要大量的猜测工作-您只需设置概率,编译器(理论上)就可以做正确的事情。 / p>
另外,clang支持__builtin_unpredictable
(从3.8开始,但是请使用__has_builtin(__builtin_unpredictable)
对其进行测试)。由于这些天很多编译器都是基于clang的,因此它也可能适用于它们。
如果您希望所有这些都准备好并准备好进行,那么您可能对我的一个项目Hedley感兴趣。它是一个公共域C / C ++标头,几乎可在所有编译器上使用,并且包含许多有用的宏,包括HEDLEY_LIKELY
,HEDLEY_UNLIKELY
,HEDLEY_UNPREDICTABLE
,HEDLEY_PREDICT
,{ {3}}和HEDLEY_PREDICT_TRUE
。它还没有C ++ 20版本,但是HEDLEY_PREDICT_FALSE
…
即使您不想在项目中使用Hedley,也可能要检查那里的实现,而不是依赖上面的列表;我可能会忘记用新信息来更新此答案,但是Hedley应该始终是最新的。
答案 6 :(得分:1)
现在 MS said 他们已经实施了 likely/unlikely attributes
但实际上,使用“可能”与不使用并没有什么区别。
我已经编译了这些代码并生成了相同的 result。
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