在我正在进行的一个大项目中,我们倾向于在错误检查中使用 gcc 的 __builtin_expect(通常当该错误将中止当前操作时):
if( __builtin_expect( failed, 0 ) )
// handle error and fail
另一方面,我们有一组(小)集中式错误函数来处理某些错误类型(即:消费者耗尽了数据)。
这些函数都以抛出异常结束,因此它们永远不会返回。
这使得我们的错误检查如下:
if( __builtin_expect( got_bytes, 0 ) )
customError( NoDataErrorCode, "No more data" ) ; // <-- always throws an exception
为了避免出现一些警告,我决定将这些函数标记为 noreturn。
通过阅读 gcc 的 __builtin_expect 和 noreturn 文档,我明白这应该没有问题,但你永远不知道。
我编写了一个小测试程序:
#include <stdexcept>
void throwIt( void ) __attribute__( ( noreturn ) ) ;
void throwIt( bool )
{
throw std::runtime_error( "forced error" ) ;
}
void iffail( bool failed )
{
if( __builtin_expect( failed, 0 ) )
throwIt( failed ) ;
}
int main( int argc, char ** )
{
iffail( !!( argc & 1 ) ) ;
return 0 ;
}
使用 -O3 -S 编译它并检查汇编代码,我发现内置函数和属性在这种特殊情况下都是无关的。删除其中任何一个(或两个)都会产生完全相同的汇编代码。这让我想知道 __builtin_expect 是否对带有简单函数调用的简单 if 语句有任何影响。
注意: throwIt 的 bool 参数是为了强制编译器执行更多操作,而不仅仅是调用 if 内的函数。
所以我的问题是:这种安排安全吗?或者是否会出现这种情况。
最佳答案
是的,很安全。如下所述,它可能会或可能不会对实际代码产生影响。
__attribute__(noreturn
) 和 __builtin_expect
的目的是帮助编译器。
如果您执行以下操作,则 noreturn 属性会避免出现“函数返回而没有 return 语句”的警告:
void panic(const char *msg) __atrribute__(noreturn);
int func(int x)
{
if (x < 0)
panic("x must not be negative");
else
return x * 42;
}
(当然,在这种特殊情况下,else
是完全多余的 - 但我确信我们可以想出一个例子,在这种情况下发生这种事情并且返回不存在)
expect“函数”帮助编译器理解“这可能发生”或“这不太可能发生”,并且编译器将根据是否可能发生选择“最佳路径”,例如:
if (failed)
throwIt( failed );
现在,如果编译器认为可能会失败,它会生成如下代码:
if (!failed) goto not_failed;
throwIt( failed );
not_failed:
do_other_stuff();
就好像编译器认为失败不太可能是真的:
if (failed) goto do_failed;
do_other_stuff();
return;
do_failed:
throwIt( failed );
(许多处理器还具有告诉分支预测单元“启动”“这被预测为真”或“这被预测为假”的位,这也可能被编码到第一个和第二种情况(如果您使用 __builtin_expect)。
当然,在您的情况下,编译器很可能“猜测”内联抛出在没有提示的情况下不太可能发生,因此无论哪种方式都会生成相同的代码段。或者它只是“巧合”地出现了这样的代码。
此外,不同的处理器以及 gcc 的目标后端都或多或少复杂,因此根据编译目标的具体情况,某些处理器与另一个处理器之间的差异可能会有很大差异。
关于c++ - gcc:混合 __builtin_expect 和 noreturn,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26023977/