可能/不太可能等同于MSVC

时间:2009-09-17 18:32:10

标签: visual-studio gcc optimization compiler-construction likely-unlikely

GCC编译器支持__builtin_expect语句,用于定义可能的和不太可能的宏。

例如

#define likely(expr)    (__builtin_expect(!!(expr), 1))
#define unlikely(expr)  (__builtin_expect(!!(expr), 0))

是否有Microsoft Visual C编译器的等效声明或等效声明?

7 个答案:

答案 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 3.0
  • c
  • ICC至少从13开始,可能更长。
  • Oracle Development Studio 12.6+,但仅在C ++模式下。
  • ARM 4.1
  • 至少从10.1开始,IBM XL C / C ++可能更长。
  • TI自6.1起
  • 从0.9.27开始的TinyCC

GCC 9+还支持__builtin_expect_with_probability。它在其他任何地方都无法使用,但希望有一天……试图弄清楚是否不恰当地/不太可能地使用它需要大量的猜测工作-您只需设置概率,编译器(理论上)就可以做正确的事情。 / p>

另外,clang支持__builtin_unpredictable(从3.8开始,但是请使用__has_builtin(__builtin_unpredictable)对其进行测试)。由于这些天很多编译器都是基于clang的,因此它也可能适用于它们。

如果您希望所有这些都准备好并准备好进行,那么您可能对我的一个项目Hedley感兴趣。它是一个公共域C / C ++标头,几乎可在所有编译器上使用,并且包含许多有用的宏,包括HEDLEY_LIKELYHEDLEY_UNLIKELYHEDLEY_UNPREDICTABLEHEDLEY_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