【问题标题】:What's the point of the probably(true) attribute可能(真实)属性的意义是什么
【发布时间】:2017-05-30 15:57:40
【问题描述】:

我刚刚在cppreference 上阅读了一些关于 C++ 中的属性的内容。他们在那里提到了可能(真实)属性,现在我想知道它有什么好处。不幸的是,我在网上找不到更多信息。

这是处理器在执行期间使用的某种分支预测吗?

【问题讨论】:

  • 这正是它的本质——查看硬件分支预测
  • 这只是一个仅适用于条件分支的属性的示例,它实际上并不以这种形式存在(据我所知)
  • 我不确定我是否理解正确,但我只是用这种语法编译了一些代码:[[probably(true)]] if (...) ... else ... 我没有检查生成的代码,但它至少编译了(使用 msc 和 c ++14)
  • @Timo 正如您链接到的 cppreference 页面所说,“实现未知的所有属性都将被忽略而不会导致错误”(该规则是为 C++17 添加的,但它只是强制执行先前的意图和实践)
  • 另外,这是一个添加这些的早期提议:ctrychta.github.io/branch_hints_proposal.html - 它只是提议[[likely]][[unlikely]]

标签: c++ attributes


【解决方案1】:

是的,没错。它用于给编译器更多关于if语句的信息,以便它可以根据目标微架构生成最优代码

虽然每个微架构都有其了解分支可能性的方法,但我们可以从英特尔优化手册中举一个简单的例子

Assembly/Compiler Coding Rule 3.(M影响,H泛化)排列代码要符合 静态分支预测算法:使条件分支之后的fall-through代码成为 可能针对具有前向目标的分支,并在条件之后生成贯穿代码 对于具有后向目标的分支来说,分支是不太可能的目标。

简单地说,前向分支的静态预测是not-taken(所以分支后的代码是推测性执行的,这是可能的路径)而后向分支是taken em> (所以分支后的代码不会被推测执行)。

考虑 GCC 的这段代码:

#define probably_true(x) __builtin_expect(!!(x), 1)
#define probably_false(x) __builtin_expect(!!(x), 0)

int foo(int a, int b, int c)
{
    if (probably_true(a==2))
        return a + b*c;
    else
        return a*b + 2*c;
}

我使用内置的__builtin_expect 来模拟[[problably(true)]]

这会被编译成

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        jne     .L2              ;If not equals jumps to .L2

        ;This is the likely path (fall-through of a forward branch)

        ;return a + b*c;

.L2:
        ;This is the unlikely path (target of a forward branch)

        ;return a*b + 2*c;

        ret

我省去了一些汇编代码。
如果将probably_true 替换为probably_false,则代码变为:

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        je     .L5               ;If equals jumps to .L5

        ;This is the likely path (fall-through of a forward branch)

        ;return a*b + 2*c;

.L5:
        ;This is the unlikely path (target of a forward branch)

        ;return a + b*c;

        ret

你可以和with this example at codebolt.org一起玩。

【讨论】:

  • @Timo 这取决于架构(或更好的微架构),在 x86 中,对于前向跳转(静态预测未采用)您的陈述是正确的,但只是第一次分支被看到。在第一次之后,分支预测器尝试检测分支的采取-未采取行为的模式。请参阅the relative article in Wikipedia 了解起点。
  • 尽管架构指南中有建议,但我的理解是在现代 x86 上没有更多的静态预测规则!。也就是说,现代分支预测器的设计方式总是只有动态预测:如果没有看到分支,则预测只是基于预测表中剩余的来自其他跳转的垃圾,或者如果根本没有任何东西,则可能是默认(可能总是失败)预测。
  • 从根本上说,静态预测规则来得太晚了——当你解码指令时,你已经获取了几行额外的指令。所以这是静态预测在很大程度上被放弃的另一个原因。动态预测机制可以在取指令之前工作。 __builtin_expect 期望的东西仍然很有用,因为它有助于代码生成 - 最可能的路径最好是直线路径,并且通常最可能的基本块组合在一起。无论如何,这大部分与(旧的)英特尔建议相吻合!
  • @MargaretBloom - 可能是,我现在什至不记得我在哪里读过它。奇怪的是,在使用您的示例时,我发现icc 如果您使用__builtin_expect 会产生terrible 代码(至少在我尝试的示例中)。它从为此循环生成optimal code(相对于gccclang,它们都失败并生成带有5 条指令的循环),到完全糟糕的代码,无论which way 你预测退出。它添加了cmov 和其他垃圾,可能是由于!!(x) 部分?
  • @BeeOnRope 我不能确定,但​​可能是,第一条指令似乎设置了eax = !!esi
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-18
  • 2015-08-08
  • 1970-01-01
  • 1970-01-01
  • 2021-04-05
  • 2015-12-24
相关资源
最近更新 更多