对于英特尔体系结构,是否有一种方法可以指示GCC编译器生成始终以特定方式强制在代码中进行分支预测的代码?英特尔硬件是否支持这一点?其他的编译器或硬件呢?
我会在C++代码中使用这一点,在这种情况下,我希望快速运行,而不关心当需要采用另一个分支时的速度减慢,即使它最近已经采用了该分支。
for (;;) {
if (normal) { // How to tell compiler to always branch predict true value?
doSomethingNormal();
} else {
exceptionalCase();
}
}
作为Evdzhan Mustafa的一个后续问题,提示能否仅指定处理器第一次遇到指令时的提示,所有后续分支预测,是否正常运行?
发布于 2020-01-15 18:36:07
从C++20开始,likely and unlikely attributes应该是标准化的,并且已经支持in g++9。因此,正如前面所讨论的here,您可以编写
if (a > b) {
/* code you expect to run often */
[[likely]] /* last statement here */
}
例如,在下面的代码中,由于if
块中的[[unlikely]]
,else块被内联
int oftendone( int a, int b );
int rarelydone( int a, int b );
int finaltrafo( int );
int divides( int number, int prime ) {
int almostreturnvalue;
if ( ( number % prime ) == 0 ) {
auto k = rarelydone( number, prime );
auto l = rarelydone( number, k );
[[unlikely]] almostreturnvalue = rarelydone( k, l );
} else {
auto a = oftendone( number, prime );
almostreturnvalue = oftendone( a, a );
}
return finaltrafo( almostreturnvalue );
}
godbolt link comparing the presence/absence of the attribute
发布于 2017-05-09 20:44:41
在C++11中定义可能/不太可能的宏的正确方法如下:
#define LIKELY(condition) __builtin_expect(static_cast<bool>(condition), 1)
#define UNLIKELY(condition) __builtin_expect(static_cast<bool>(condition), 0)
与[[likely]]
不同,此方法与所有C++版本兼容,但依赖于非标准扩展__builtin_expect
。
当这些宏以这种方式定义时:
#define LIKELY(condition) __builtin_expect(!!(condition), 1)
这可能会改变if
语句的含义并破坏代码。考虑以下代码:
#include <iostream>
struct A
{
explicit operator bool() const { return true; }
operator int() const { return 0; }
};
#define LIKELY(condition) __builtin_expect((condition), 1)
int main() {
A a;
if(a)
std::cout << "if(a) is true\n";
if(LIKELY(a))
std::cout << "if(LIKELY(a)) is true\n";
else
std::cout << "if(LIKELY(a)) is false\n";
}
及其输出:
if(a) is true
if(LIKELY(a)) is false
如您所见,可能使用!!
作为对bool
的强制转换的定义打破了if
的语义。
这里的重点不是operator int()
和operator bool()
应该是相关的。这是一个很好的实践。
相反,使用!!(x)
而不是static_cast<bool>(x)
会丢失C++11 contextual conversions的上下文。
发布于 2015-05-09 18:02:39
正如其他答案所充分建议的那样,您可以使用__builtin_expect
给编译器一个关于如何排列汇编代码的提示。正如the official docs指出的那样,在大多数情况下,内置在你大脑中的汇编程序不会像GCC团队精心制作的汇编程序那么好。最好是使用实际的配置文件数据来优化您的代码,而不是猜测。
沿着类似的路线,但尚未提到,是一种特定于GCC的方法,强制编译器在“冷”路径上生成代码。这涉及到noinline
和cold
属性的使用,这两个属性的作用与它们听起来完全一样。这些属性只能应用于函数,但是使用C++11,您可以声明内联的lambda函数,并且这两个属性也可以应用于lambda函数。
虽然这仍然属于微优化的一般类别,因此标准建议适用-测试不要猜测-我觉得它比__builtin_expect
更有用。几乎没有几代x86处理器使用分支预测提示(reference),因此您唯一能够影响的就是汇编代码的顺序。因为您知道什么是错误处理或“边缘情况”代码,所以可以使用此注释来确保编译器不会预测到它的分支,并在优化大小时将其链接到“热”代码。
示例用法:
void FooTheBar(void* pFoo)
{
if (pFoo == nullptr)
{
// Oh no! A null pointer is an error, but maybe this is a public-facing
// function, so we have to be prepared for anything. Yet, we don't want
// the error-handling code to fill up the instruction cache, so we will
// force it out-of-line and onto a "cold" path.
[&]() __attribute__((noinline,cold)) {
HandleError(...);
}();
}
// Do normal stuff
⋮
}
更好的是,当配置文件反馈可用时(例如,当使用-fprofile-use
编译时),GCC将自动忽略它,而支持配置文件反馈。
请在此处查看官方文档:https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
https://stackoverflow.com/questions/30130930
复制相似问题