Wil*_*mKF 113 c++ gcc intel pragma branch-prediction
对于英特尔架构,是否有一种方法可以指示GCC编译器生成的代码总是强制分支预测在我的代码中采用特定方式?英特尔硬件是否支持此功能?那么其他编译器或硬件呢?
我会在C++代码中使用它,我知道我希望快速运行的情况,并且不关心当另一个分支需要被采取时,即使它最近采用了该分支.
for (;;) {
if (normal) { // How to tell compiler to always branch predict true value?
doSomethingNormal();
} else {
exceptionalCase();
}
}
Run Code Online (Sandbox Code Playgroud)
作为Evdzhan Mustafa的后续问题,该提示是否可以在处理器第一次遇到指令时指定一个提示,所有后续的分支预测都能正常运行?
Jac*_*ack 83
GCC支持__builtin_expect(long exp, long c)提供此类功能的功能.您可以在此处查看文档.
exp使用的条件在哪里,c是预期值.例如,你想要的情况
if (__builtin_expect(normal, 1))
Run Code Online (Sandbox Code Playgroud)
由于笨拙的语法,通常通过定义两个自定义宏来使用它
#define likely(x) __builtin_expect (!!(x), 1)
#define unlikely(x) __builtin_expect (!!(x), 0)
Run Code Online (Sandbox Code Playgroud)
只是为了缓解任务.
请注意:
Sha*_*our 44
gcc有很长的__builtin_expect(long exp,long c)(强调我的):
您可以使用__builtin_expect为编译器提供分支预测信息.一般来说,你应该更喜欢使用实际的配置文件反馈(-fprofile-arcs),因为程序员在预测程序实际执行情况方面是出了名的.但是,有些应用程序难以收集此数据.
返回值是exp的值,它应该是一个整数表达式.内置的语义是期望exp == c.例如:
Run Code Online (Sandbox Code Playgroud)if (__builtin_expect (x, 0)) foo ();表示我们不期望调用foo,因为我们期望x为零.由于您仅限于exp的整数表达式,因此您应该使用诸如的结构
Run Code Online (Sandbox Code Playgroud)if (__builtin_expect (ptr != NULL, 1)) foo (*ptr);在测试指针或浮点值时.
正如文档中指出的那样,您应该更喜欢使用实际的配置文件反馈,本文将展示一个实际的示例,以及它在这种情况下如何最终成为对使用的改进__builtin_expect.另请参阅如何在g ++中使用配置文件引导的优化?.
我们还可以找到关于使用此功能的kernal宏可能()和不太可能()的Linux内核新手文章:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
Run Code Online (Sandbox Code Playgroud)
注意!!宏中使用的我们可以在为什么使用!!(条件)而不是(条件)中找到对此的解释?.
仅仅因为在Linux内核中使用这种技术并不意味着使用它总是有意义的.我们可以从这个问题中看到,我最近回答了在将参数作为编译时常量或变量传递时的函数性能之间的差异,许多手动优化技术在一般情况下不起作用.我们需要仔细分析代码以了解技术是否有效.许多旧技术甚至可能与现代编译器优化无关.
注意,虽然builtins不是便携式,但clang也支持__builtin_expect.
对某些架构而言,它可能没有什么区别.
Art*_*ius 41
不,那里没有.(至少在现代x86处理器上.)
__builtin_expect在其他答案中提到的影响gcc安排汇编代码的方式.它不会直接影响CPU的分支预测器.当然,通过重新排序代码会对分支预测产生间接影响.但是在现代的x86处理器上,没有指令告诉CPU"假设这个分支是/不被采用".
有关更多详细信息,请参阅此问题:英特尔x86 0x2E/0x3E前缀分支预测实际使用?
要明确__builtin_expect和/或使用-fprofile-arcs 可以提高代码的性能,通过代码布局给分支预测器提供提示(请参阅x86-64程序集的性能优化 - 对齐和分支预测),还可以改进缓存行为保持"不太可能"的代码远离"可能的"代码.
Max*_*kin 22
在C++ 11中定义可能/不太可能的宏的正确方法如下:
#define LIKELY(condition) __builtin_expect(static_cast<bool>(condition), 1)
#define UNLIKELY(condition) __builtin_expect(static_cast<bool>(condition), 0)
Run Code Online (Sandbox Code Playgroud)
当这些宏以这种方式定义时:
#define LIKELY(condition) __builtin_expect(!!(condition), 1)
Run Code Online (Sandbox Code Playgroud)
这可能会改变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";
}
Run Code Online (Sandbox Code Playgroud)
它的输出:
if(a) is true
if(LIKELY(a)) is false
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,LIKELY的定义使用强制转换!!来bool破坏语义if.
这里的要点不是那个operator int(),operator bool()应该是相关的.哪个是好习惯.
而是使用!!(x)而不是static_cast<bool>(x)丢失C++ 11上下文转换的上下文.
Cod*_*ray 15
由于其他答案都已充分建议,您可以使用__builtin_expect给编译器提供有关如何排列汇编代码的提示.正如官方文档指出的那样,在大多数情况下,构建在大脑中的汇编程序不如GCC团队制作的那样好.最好使用实际的配置文件数据来优化代码,而不是猜测.
沿着类似的路线,但尚未提及,是一种特定于GCC的方法,迫使编译器在"冷"路径上生成代码.这涉及到noinline和cold属性的使用,这些属性与他们听起来完全一样.这些属性只能应用于函数,但是使用C++ 11,您可以声明内联lambda函数,这两个属性也可以应用于lambda函数.
虽然这仍然属于微优化的一般类别,因此标准建议适用 - 测试不要猜测 - 我觉得它比一般更有用__builtin_expect.几乎没有任何代x86处理器使用分支预测提示(参考),因此您唯一能够影响的是汇编代码的顺序.由于您知道什么是错误处理或"边缘情况"代码,因此您可以使用此批注来确保编译器不会预测到它的分支,并在优化大小时将其与"热"代码链接起来.
样品用法:
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
?
}
Run Code Online (Sandbox Code Playgroud)
更好的是,当GCC可用时(例如,编译时-fprofile-use),GCC会自动忽略这一点以支持配置文件反馈.
请参阅此处的官方文档:https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
pse*_*ert 11
从 C++20 开始,可能和不太可能的属性应该被标准化,并且已经在 g++9 中得到支持。所以正如这里所讨论的,你可以写
if (a > b) {
/* code you expect to run often */
[[likely]] /* last statement here */
}
Run Code Online (Sandbox Code Playgroud)
例如,在下面的代码中,由于[[unlikely]]inif块,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 );
}
Run Code Online (Sandbox Code Playgroud)
__builtin_expect 可用于告诉编译器您期望分支的走向。这会影响代码的生成方式。典型的处理器按顺序运行代码更快。所以如果你写
if (__builtin_expect (x == 0, 0)) ++count;
if (__builtin_expect (y == 0, 0)) ++count;
if (__builtin_expect (z == 0, 0)) ++count;
Run Code Online (Sandbox Code Playgroud)
编译器会生成类似的代码
if (x == 0) goto if1;
back1: if (y == 0) goto if2;
back2: if (z == 0) goto if3;
back3: ;
...
if1: ++count; goto back1;
if2: ++count; goto back2;
if3: ++count; goto back3;
Run Code Online (Sandbox Code Playgroud)
如果您的提示正确,这将执行代码而不实际执行任何分支。它将比正常序列运行得更快,其中每个 if 语句将围绕条件代码分支并执行三个分支。
较新的 x86 处理器具有针对预期采用的分支或预期不采用的分支的指令(有一个指令前缀;不确定详细信息)。不确定处理器是否使用它。它不是很有用,因为分支预测可以很好地处理这个问题。所以我认为你实际上不能影响分支预测。
| 归档时间: |
|
| 查看次数: |
19325 次 |
| 最近记录: |