我有一个自定义ASSERT(...)
宏,我在C ++应用程序中使用它。
#include <stdlib.h>
#include <iostream>
/// ASSERT(expr) checks if expr is true. If not, error details are logged
/// and the process is exited with a non-zero code.
#ifdef INCLUDE_ASSERTIONS
#define ASSERT(expr) \
if (!(expr)) { \
char buf[4096]; \
snprintf (buf, 4096, "Assertion failed in \"%s\", line %d\n%s\n", \
__FILE__, __LINE__, #expr); \
std::cerr << buf; \
::abort(); \
} \
else // This 'else' exists to catch the user's following semicolon
#else
#define ASSERT(expr)
#endif
最近我正在阅读一些Linux内核模块代码,并且遇到了likely(...)
和unlikely(...)
宏的存在。这些提示给CPU提示给定分支更有可能,并且管道应该针对该路径进行优化。
根据定义,断言应该评估为真(即likely
)。
我可以在ASSERT
宏中提供类似的提示吗?这里的潜在机制是什么?
显然我会衡量表现的任何差异,但理论上它应该有所不同吗?
我只在Linux上运行我的代码,但是有兴趣知道是否有跨平台的方式来做这件事。我也在使用gcc,但也想支持clang。
答案 0 :(得分:14)
性能增益可能不大,但这就是定义这些linux内核宏的方法:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
因此,您可以像这样修改您的条件(假设expr
预期为真,因此预期!(expr)
为假):
if (__builtin_expect(!(expr), 0)) {
或者您可以定义与内核相同的宏,并使用它们以提高可读性。
这是gcc内置的,当然不可移植。
This表明clang也支持内置。不过,您可以使用上面的宏并在不支持内置的编译器上有条件地定义它们,如#define likely(x) (x)
。
在你的情况下,预测会很好(或者你或者你正在中止),所以不应该有悲观的风险,但如果你考虑更广泛地使用内置,这里有来自gcc文档的建议:
一般情况下,您应该更喜欢使用实际的配置文件反馈(-fprofile-arcs),因为程序员在预测程序实际执行情况方面非常糟糕。
答案 1 :(得分:10)
对于许多CPU,likely
和unlikely
(或其他任何事情)不向CPU提供分支提示(仅限于编译器,可能使用它来进行不同的优化,类似于配置文件引导优化),原因很简单,没有办法做到这一点。
例如,自P4起为x86定义了分支提示。在此之前他们没有任何影响,但更糟糕的是,他们对P4以外的任何事情都没有影响。所以它们没用(但浪费空间和带宽),据我所知,GCC不发射它们。
ARM不(但是?)也有分支提示。 PPC,IA64和SPARC确实有暗示分支,我不知道GCC是否会使用likely
和unlikely
,但至少它可以。
答案 2 :(得分:2)
无需任何其他注释。编译器已经很少知道abort
被称为非常(每个程序执行最多一次),因此编译器会将包含abort
的分支视为不可能的分支。您可以通过查看abort
的声明来验证这一点。在glibc中,它被声明为
extern void abort (void) __THROW __attribute__ ((__noreturn__));
并在Visual Studio 2013中:
_CRTIMP __declspec(noreturn) void __cdecl abort(void);