如何在if-else语句中使用C++20的likely/unlikely属性

49
这个问题涉及到C++20的[[likely]]/[[unlikely]]特性,而不是编译器定义的宏。
这份文档(cppreference)只提供了将它们应用于switch-case语句的示例。这个switch-case示例在我的编译器(g++-7.2)上完美编译,所以我认为编译器实现了这个特性,尽管它还未正式引入当前的C++标准。
但当我像这样使用它们:if (condition) [[likely]] { ... } else { ... },我会收到警告:

"warning: attributes at the beginning of statement are ignored [-Wattributes]".

那么我应该如何在if-else语句中使用这些属性呢?

如果一个条件在if语句中更有可能被满足,那么请将其放在首位。 - Mercer
4
我知道你的意思,但这不是我询问的内容。 - Leedehai
3
可以只部分实现。首先查看 g++ 文档,看看它是否支持该功能。 - molbdnilo
1
正如molbdnillo所说,由于c++20标准尚未完成,更不用说发布了,因此将新功能集成到编译器中是由编译器自行决定的。 - Mercer
你有可以展示给我们的例子吗? - SherylHohman
@Mercer 将最有可能的条件放在第一条语句中并不等同于使用[[likely]]。有时编译器会将if(...) A else B更改为if (not ...) B else A。这将破坏流水线,导致速度变慢。以下是一个示例:https://godbolt.org/z/WcPbPv - HarryLeong
3个回答

27

好的,目前编译器只部分实现了这个属性。 - Leedehai
@Leedehai,我认为目前对于即将到来的C++20标准的支持,总体上仍然是纯粹的实验性质。 - user7860670
我发现有趣的是,在那个链接中提供的 while 循环示例,将 likely token 放在右括号之后,而不像其他示例一样放在前面。 - Mercer
1
@Mercer,最新的ISO草案只是说明属性可以分配给标签或语句,因此似乎 while (cond) [[likely]] { x(); y(); }while (cond) { [[likely]] x(); y(); } 是同样有效的。我不确定仅将第一个语句标记为复合语句是否有多大意义,但也许委员会知道得更多 - 实际上,这似乎是很可能的,现在我想起来了 :-) - paxdiablo
我刚刚查看了提供的草稿中的示例,它完全省略了 while 循环的示例。时间会证明一切。 - Mercer
@Mercer,目前来看可能是允许的,至少在草案中是这样规定的:“属性标记likely和unlikely可以应用于标签或语句”(语句可以是简单的或复合的)。但我不确定在序列中一个单独的[[likely]]语句的价值是什么,除非序列可以被打破(也许通过异常或exit()?)。就像你说的,时间会证明一切... - paxdiablo

7
截至今日,cppreference表示,例如likely(强调是我的):
应用于语句,允许编译器针对包括该语句的执行路径优化,这些路径比不包括此类语句的任何替代执行路径更可能出现。
这表明要放置属性的位置是最有可能的语句,即:语句
if (condition) { [[likely]] ... } else { ... }

例如,在使用/std:c++latest编译时,Visual Studio 2019 16.7.0会接受此语法。


这段代码的结果是什么:if (cond) { if (cond2) [[likely]] { f(); } else { g(); } } else { h(); }?说实话,很难确定哪一个被理解为 likely?是外部的 true 分支还是内部的 true 分支? - xryl669

5
那么我在if-else语句中该如何使用这些属性呢?与您目前的做法完全相同,根据草案标准中给出的示例,您的语法是正确的(为了显示相关部分而简化):
int f(int n) {
    if (n > 5) [[unlikely]] {
        g(0);
        return n * 2 + 1;
    }

    return 3;
}

但是你应该明白,这个功能是相对较新的,因此可能只有一些实现中有占位符来允许你设置属性。从你的警告信息中可以看出这一点。
你还应该明白,除非最新草案和最终产品之间的某些措辞发生变化,即使符合规范的实现也可以忽略这些属性。它们非常像C语言中的inline,是编译器的建议。根据最新草案n4762(在本答案撰写时),如下所示:

注意:likely属性的使用旨在允许实现针对包含它的执行路径进行优化,这些路径的执行概率任意高于不在语句或标签上包含此类属性的任何其他执行路径。

请注意,“允许”而不是“强制”、“要求”或“命令”这个词。

15
如果我们可以在if语句内指定属性,例如if ([[likely]] a>b) {,就会非常容易移植基于__builtin_expect的现有代码,比如if (likely(a>b)) { - rustyx
1
@rustyx 但是该语法也适用于switch case,在此情况下比if更加实用。我使用它获得了良好的性能提升。 - Octo Poulos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接