Подсказка для прогноза ветвления в утверждениях
У меня есть пользовательский макрос ASSERT(...)
, который я использую в приложении С++.
#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 following semicolon
#else
#define ASSERT(expr)
#endif
Недавно я читал код модуля ядра Linux и сталкивался с наличием макросов likely(...)
и unlikely(...)
. Они дают указание процессору, что данная ветка более вероятна, и что конвейер должен оптимизировать для этого пути.
Утверждения, по определению, должны оцениваться как истинные (т.е. likely
).
Могу ли я предоставить аналогичный намек в моем макросе ASSERT
? Какой здесь основной механизм?
Очевидно, я буду измерять любую разницу в производительности, но теоретически это должно иметь какое-то значение?
Я только запускаю свой код в Linux, но мне было бы интересно узнать, есть ли кросс-платформенный способ сделать это тоже. Я также использую gcc, но также хотел бы поддержать clang.
Ответы
Ответ 1
Увеличение производительности вряд ли будет значительным, но именно так определяются макросы ядра Linux:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
Итак, вы можете изменить свое условие следующим образом (предполагая, что expr
ожидается как истина, и поэтому !(expr)
ожидается false):
if (__builtin_expect(!(expr), 0)) {
Или вы можете определить те же макросы, что и ядро, и использовать их для лучшей читаемости.
Это gcc builtin, поэтому не переносится, конечно.
Это говорит о том, что clang также поддерживает встроенный. Кстати, вы можете использовать указанные выше макросы и условно определить их как #define likely(x) (x)
для компиляторов, которые не поддерживают встроенный.
В вашем случае предсказание будет хорошим (либо это, либо вы прерываете), поэтому не должно быть риска пессимизации, но если вы решите использовать встроенный более широко, здесь слово рекомендации по документации gcc:
В общем, вы должны предпочесть использовать фактическую обратную связь для этого (-fprofile-arcs), поскольку программисты, как известно, плохо прогнозируют, как их программы действительно выполняются.
Ответ 2
Для многих процессоров likely
и unlikely
(или что-то еще в этом случае) не предоставляют подсказки ветки процессору (только для компилятора, который может использовать его для оптимизации по-разному, подобно профилю, управляемому профилем оптимизация) по той простой причине, что нет способа сделать это.
Например, подсказки ветки определены для x86 с P4. До этого они не имели никакого эффекта, но это еще хуже, они не влияют ни на что, кроме P4. Поэтому они бесполезны (но пространство для отходов и пропускная способность), и насколько я знаю, GCC не испускает их.
В ARM нет (еще?) есть подсказки ветвления. PPC, IA64 и SPARC имеют намеки на ветки, я не знаю, использует ли GCC likely
и unlikely
для них, но, по крайней мере, это может быть.
Ответ 3
Нет никакой дополнительной аннотации. Компилятор уже знает о abort
, который вызывается очень редко (не более одного раза для выполнения программы), поэтому компилятор будет рассматривать ветвь, содержащую abort
как маловероятную ветвь. Вы можете проверить это, посмотрев объявление abort
. В glibc объявляется как
extern void abort (void) __THROW __attribute__ ((__noreturn__));
и в Visual Studio 2013:
_CRTIMP __declspec(noreturn) void __cdecl abort(void);